I've noticed several complaints about two commands trying to report their progress at the same time, overwriting each other's output [1][2][3]. I posted a prototype of an enhancement of the monitoring infrastructure that allows for progress reporting of two commands w/o conflicts [4].
This is a new version that passes regression tests. I'll register it for the upcoming CF. [1] https://www.postgresql.org/message-id/5667.1774513434%40localhost [2] https://www.postgresql.org/message-id/CALj2ACUgwSchK6jQ2CdKLBWUADTOE_zKdTff2Zg3E6hOuXKv-w%40mail.gmail.com [3] https://www.postgresql.org/message-id/aj4gJQMba0kClQmj%40Mac [4] https://www.postgresql.org/message-id/30939.1777888333%40localhost -- Antonin Houska Web: https://www.cybertec-postgresql.com
>From 6595aea491b5f69e9205bbb72231435307e3a6ea Mon Sep 17 00:00:00 2001 From: Antonin Houska <[email protected]> Date: Mon, 29 Jun 2026 13:10:49 +0200 Subject: [PATCH] Allow progress tracking of sub-commands. Some commands that support progress reporting run other commands which also need to report their progress. The typical case is that REPACK ("main" command) runs index build (sub-command). The current approach is to disable progress tracking when a command is running as a sub-command of another command. However, the progress reporting can be AM-specific, so it's not really practicable to suppress the progress reporting of the sub-commands entirely. Even if we managed to pass a flag down to all the relevant places, we could end up with progress reporting for the sub-command disabled in cases it's important to the user. Instead of disabling the progress tracking of the sub-commands, this patch allows both the main command and the sub-command to report their progress at the same time. --- contrib/file_fdw/file_fdw.c | 44 +++++++++++ src/backend/access/brin/brin.c | 3 + src/backend/access/transam/xact.c | 33 +++++++- src/backend/bootstrap/bootstrap.c | 11 ++- src/backend/catalog/heap.c | 13 ++- src/backend/catalog/index.c | 50 ++++++------ src/backend/catalog/toasting.c | 9 +++ src/backend/commands/indexcmds.c | 62 ++++++++------- src/backend/commands/repack.c | 35 ++++++-- src/backend/utils/activity/backend_progress.c | 79 ++++++++++++++++--- src/backend/utils/activity/backend_status.c | 1 + src/backend/utils/adt/pgstatfuncs.c | 15 +++- src/include/catalog/index.h | 9 +-- src/include/utils/backend_status.h | 19 +++++ 14 files changed, 294 insertions(+), 89 deletions(-) diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 33a37d832ce..a60fb226320 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -36,6 +36,7 @@ #include "optimizer/pathnode.h" #include "optimizer/planmain.h" #include "optimizer/restrictinfo.h" +#include "utils/backend_status.h" #include "utils/acl.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -119,6 +120,16 @@ typedef struct FileFdwExecutionState CopyFromState cstate; /* COPY execution state */ } FileFdwExecutionState; +/* + * Since progress tracking of multiple COPY commands is not supported, the + * first file_fdw node of the plan needs to set pgstat_track_activities to + * false during startup, and the last active node needs to restore the + * original value during shutdown. + */ +static bool save_pgstat_track_activities = false; +static int fdw_nodes = 0; +static int active_fdw_nodes = 0; + /* * SQL functions */ @@ -616,6 +627,12 @@ fileGetForeignPlan(PlannerInfo *root, { Index scan_relid = baserel->relid; + /* + * This seems to be the appropriate place to count file_fdw nodes in the + * plan. + */ + fdw_nodes++; + /* * We have no native ability to evaluate restriction clauses, so we just * put all the scan_clauses into the plan node's qual list for the @@ -695,6 +712,18 @@ fileBeginForeignScan(ForeignScanState *node, int eflags) /* Add any options from the plan (currently only convert_selectively) */ options = list_concat(options, plan->fdw_private); + /* + * Save the value of pgstat_track_activities if this is the first file_fdw + * node of a plan containing multiple file_fdw nodes, and disable the + * progress tracking. The monitoring infrastructure currently does not + * support monitoring of multiple COPY commands. + */ + if (fdw_nodes > 1 && active_fdw_nodes++ == 0) + { + save_pgstat_track_activities = pgstat_track_activities; + pgstat_track_activities = false; + } + /* * Create CopyState from FDW options. We always acquire all columns, so * as to match the expected ScanTupleSlot signature. @@ -861,6 +890,21 @@ fileEndForeignScan(ForeignScanState *node) festate->cstate->num_errors)); EndCopyFrom(festate->cstate); + + + /* + * Restore the value of pgstat_track_activities if this is the last + * file_fdw node of a plan containing multiple file_fdw nodes, and enable + * progress tracking if we disabled it earlier. + */ + if (active_fdw_nodes > 0) + { + if (--active_fdw_nodes == 0) + { + pgstat_track_activities = save_pgstat_track_activities; + fdw_nodes = 0; + } + } } /* diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..f84b992478a 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -2847,8 +2847,11 @@ _brin_parallel_scan_and_build(BrinBuildState *state, ParallelTableScanFromBrinShared(brinshared), SO_NONE); + pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, + RelationGetRelid(heap)); reltuples = table_index_build_scan(heap, index, indexInfo, true, true, brinbuildCallbackParallel, state, scan); + pgstat_progress_end_command(); /* insert the last item */ form_and_spill_tuple(state); diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 3a89149016f..78a77fe4bb3 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -217,6 +217,8 @@ typedef struct TransactionStateData bool parallelChildXact; /* is any parent transaction parallel? */ bool chain; /* start a new block after this one */ bool topXidLogged; /* for a subxact: is top-level XID logged? */ + int progress_depth; /* progress tracking state before subxact + * started */ struct TransactionStateData *parent; /* back link to parent */ } TransactionStateData; @@ -2253,6 +2255,12 @@ StartTransaction(void) */ s->state = TRANS_INPROGRESS; + /* + * Top transaction always terminates progress tracking on abort, so this + * field is not needed, but be tidy. + */ + s->progress_depth = 0; + /* Schedule transaction timeout */ if (TransactionTimeout > 0) enable_timeout_after(TRANSACTION_TIMEOUT, TransactionTimeout); @@ -5139,6 +5147,12 @@ StartSubTransaction(void) s->state = TRANS_INPROGRESS; + /* + * Subtransaction needs to know if it should cancel progress reporting on + * abort. + */ + s->progress_depth = PGSTAT_PROGRESS_STATE(MyBEEntry); + /* * Call start-of-subxact callbacks */ @@ -5298,7 +5312,24 @@ AbortSubTransaction(void) WaitLSNCleanup(); pgstat_report_wait_end(); - pgstat_progress_end_command(); + + /* + * Abort of a subtransaction does not imply the end of monitoring for the + * whole transaction. Instead, we only cancel monitoring started within + * the subtransaction. + * + * One particular case it matters is an index function that catches an + * exception and lets the index build continue, so the abort should not + * cancel monitoring of that build. + * + * XXX Shouldn't we restore the progress state as it was before the + * subtransaction started? Not sure about use case - the index functions + * mentioned above probably don't start their own progress tracking. On + * the other hand, a monitored command started in a subtransaction usually + * does not have a parent command in the parent transaction. + */ + for (int i = PGSTAT_PROGRESS_STATE(MyBEEntry); i > s->progress_depth; i--) + pgstat_progress_end_command(); pgaio_error_cleanup(); diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index b0dcd9876c5..d15a06aa99c 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -40,6 +40,7 @@ #include "storage/ipc.h" #include "storage/proc.h" #include "storage/shmem_internal.h" +#include "utils/backend_progress.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/guc.h" @@ -1188,7 +1189,15 @@ build_indices(void) heap = table_open(ILHead->il_heap, NoLock); ind = index_open(ILHead->il_ind, NoLock); - index_build(heap, ind, ILHead->il_info, false, false, false); + /* + * Progress tracking is not really needed here, but it's simpler to + * start it than to disable it everywhere (including the specific + * AMs). + */ + pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, + RelationGetRelid(heap)); + index_build(heap, ind, ILHead->il_info, false, false); + pgstat_progress_end_command(); index_close(ind, NoLock); table_close(heap, NoLock); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 88087654de9..5339cfa51de 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -3588,10 +3588,15 @@ RelationTruncateIndexes(Relation heapRelation) */ RelationTruncate(currentIndex, 0); - /* Initialize the index and rebuild */ - /* Note: we do not need to re-establish pkey setting */ - index_build(heapRelation, currentIndex, indexInfo, true, false, - true); + /* + * Initialize the index and rebuild + * + * Note: we do not need to re-establish pkey setting + */ + pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, + RelationGetRelid(heapRelation)); + index_build(heapRelation, currentIndex, indexInfo, true, false); + pgstat_progress_end_command(); /* We're done with this index */ index_close(currentIndex, NoLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..e5d12308b6e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -715,8 +715,6 @@ UpdateIndexRelation(Oid indexoid, * already exists. * INDEX_CREATE_PARTITIONED: * create a partitioned index (table must be partitioned) - * INDEX_CREATE_SUPPRESS_PROGRESS: - * don't report progress during the index build. * * constr_flags: flags passed to index_constraint_create * (only if INDEX_CREATE_ADD_CONSTRAINT is set) @@ -763,7 +761,6 @@ index_create(Relation heapRelation, bool invalid = (flags & INDEX_CREATE_INVALID) != 0; bool concurrent = (flags & INDEX_CREATE_CONCURRENT) != 0; bool partitioned = (flags & INDEX_CREATE_PARTITIONED) != 0; - bool progress = (flags & INDEX_CREATE_SUPPRESS_PROGRESS) == 0; char relkind; TransactionId relfrozenxid; MultiXactId relminmxid; @@ -1280,8 +1277,7 @@ index_create(Relation heapRelation, } else { - index_build(heapRelation, indexRelation, indexInfo, false, true, - progress); + index_build(heapRelation, indexRelation, indexInfo, false, true); } /* @@ -1540,7 +1536,7 @@ index_concurrently_build(Oid heapRelationId, indexInfo->ii_BrokenHotChain = false; /* Now build the index */ - index_build(heapRel, indexRelation, indexInfo, false, true, true); + index_build(heapRel, indexRelation, indexInfo, false, true); /* Roll back any GUC changes executed by index functions */ AtEOXact_GUC(false, save_nestlevel); @@ -2221,6 +2217,8 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode) */ if (concurrent) { + bool progress; + /* * We must commit our transaction in order to make the first pg_index * state update visible to other sessions. If the DROP machinery has @@ -2291,11 +2289,12 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode) * to acquire an exclusive lock on our table. The lock code will * detect deadlock and error out properly. * - * Note: we report progress through WaitForLockers() unconditionally - * here, even though it will only be used when we're called by REINDEX - * CONCURRENTLY and not when called by DROP INDEX CONCURRENTLY. + * Note: we report progress through WaitForLockers() only when the + * command is REINDEX CONCURRENTLY. There's no progress reporting for + * DROP INDEX CONCURRENTLY. */ - WaitForLockers(heaplocktag, AccessExclusiveLock, true); + progress = MyBEEntry->st_progress_command == PROGRESS_COMMAND_CREATE_INDEX; + WaitForLockers(heaplocktag, AccessExclusiveLock, progress); /* * Updating pg_index might involve TOAST table access, so ensure we @@ -2319,7 +2318,7 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode) * Wait till every transaction that saw the old index state has * finished. See above about progress reporting. */ - WaitForLockers(heaplocktag, AccessExclusiveLock, true); + WaitForLockers(heaplocktag, AccessExclusiveLock, progress); /* * Re-open relations to allow us to complete our actions. @@ -3011,7 +3010,6 @@ index_update_stats(Relation rel, * * isreindex indicates we are recreating a previously-existing index. * parallel indicates if parallelism may be useful. - * progress indicates if the backend should update its progress info. * * Note: before Postgres 8.2, the passed-in heap and index Relations * were automatically closed by this routine. This is no longer the case. @@ -3022,8 +3020,7 @@ index_build(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, bool isreindex, - bool parallel, - bool progress) + bool parallel) { IndexBuildResult *stats; Oid save_userid; @@ -3074,7 +3071,6 @@ index_build(Relation heapRelation, RestrictSearchPath(); /* Set up initial progress report status */ - if (progress) { const int progress_index[] = { PROGRESS_CREATEIDX_PHASE, @@ -3639,7 +3635,6 @@ reindex_index(const ReindexStmt *stmt, Oid indexId, IndexInfo *indexInfo; volatile bool skipped_constraint = false; PGRUsage ru0; - bool progress = ((params->options & REINDEXOPT_REPORT_PROGRESS) != 0); bool set_tablespace = false; pg_rusage_init(&ru0); @@ -3674,7 +3669,6 @@ reindex_index(const ReindexStmt *stmt, Oid indexId, save_nestlevel = NewGUCNestLevel(); RestrictSearchPath(); - if (progress) { const int progress_cols[] = { PROGRESS_CREATEIDX_COMMAND, @@ -3713,9 +3707,8 @@ reindex_index(const ReindexStmt *stmt, Oid indexId, return; } - if (progress) - pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID, - iRel->rd_rel->relam); + pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID, + iRel->rd_rel->relam); /* * If a statement is available, telling that this comes from a REINDEX @@ -3832,7 +3825,7 @@ reindex_index(const ReindexStmt *stmt, Oid indexId, /* Initialize the index and rebuild */ /* Note: we do not need to re-establish pkey setting */ - index_build(heapRelation, iRel, indexInfo, true, true, progress); + index_build(heapRelation, iRel, indexInfo, true, true); /* Re-allow use of target index */ ResetReindexProcessing(); @@ -3926,8 +3919,7 @@ reindex_index(const ReindexStmt *stmt, Oid indexId, index_close(iRel, NoLock); table_close(heapRelation, NoLock); - if (progress) - pgstat_progress_end_command(); + pgstat_progress_end_command(); } /* @@ -4099,9 +4091,15 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, /* Index should no longer be in the pending list */ Assert(!ReindexIsProcessingIndex(indexOid)); - /* Set index rebuild count */ - pgstat_progress_update_param(PROGRESS_REPACK_INDEX_REBUILD_COUNT, - i); + /* + * Set index rebuild count - only interesting for REPACK. + * + * XXX Shouldn't we add an argument indicating that the function is + * being called from REPACK (or its subroutine)? + */ + if (MyBEEntry->st_progress_command == PROGRESS_COMMAND_REPACK) + pgstat_progress_update_param(PROGRESS_REPACK_INDEX_REBUILD_COUNT, + i); i++; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 4aa52a4bd25..672dcba84f4 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -30,6 +30,7 @@ #include "catalog/toasting.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "utils/backend_progress.h" #include "utils/fmgroids.h" #include "utils/rel.h" #include "utils/syscache.h" @@ -325,6 +326,13 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, coloptions[0] = 0; coloptions[1] = 0; + /* + * XXX Progress reporting makes little sense for empty table, but + * index_create() does report the progress and it's not worth disabling it + * in this specific case. + */ + pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, + RelationGetRelid(toast_rel)); index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid, InvalidOid, InvalidOid, indexInfo, @@ -333,6 +341,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, rel->rd_rel->reltablespace, collationIds, opclassIds, NULL, coloptions, NULL, (Datum) 0, INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL); + pgstat_progress_end_command(); table_close(toast_rel, NoLock); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..c2d4b9ff087 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -594,6 +594,7 @@ DefineIndex(ParseState *pstate, Oid root_save_userid; int root_save_sec_context; int root_save_nestlevel; + bool progress_started = false; root_save_nestlevel = NewGUCNestLevel(); @@ -622,16 +623,21 @@ DefineIndex(ParseState *pstate, concurrent = false; /* - * Start progress report. If we're building a partition, this was already - * done. + * Start progress report if not done yet. + * + * We could check parentIndexId and only start the reporting when + * processing the parent index (i.e. parentIndexId is invalid), however + * that wouldn't work when adding a partition to an existing table: + * parentIndexId is always valid in that cases. */ - if (!OidIsValid(parentIndexId)) + if (MyBEEntry->st_progress_command == PROGRESS_COMMAND_INVALID) { pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, tableId); pgstat_progress_update_param(PROGRESS_CREATEIDX_COMMAND, concurrent ? PROGRESS_CREATEIDX_COMMAND_CREATE_CONCURRENTLY : PROGRESS_CREATEIDX_COMMAND_CREATE); + progress_started = true; } /* @@ -1277,8 +1283,7 @@ DefineIndex(ParseState *pstate, table_close(rel, NoLock); - /* If this is the top-level index, we're done */ - if (!OidIsValid(parentIndexId)) + if (progress_started) pgstat_progress_end_command(); return address; @@ -1585,21 +1590,18 @@ DefineIndex(ParseState *pstate, } } - /* - * Indexes on partitioned tables are not themselves built, so we're - * done here. - */ AtEOXact_GUC(false, root_save_nestlevel); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); table_close(rel, NoLock); - if (!OidIsValid(parentIndexId)) - pgstat_progress_end_command(); - else + if (OidIsValid(parentIndexId)) { /* Update progress for an intermediate partitioned index itself */ pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1); } + if (progress_started) + pgstat_progress_end_command(); + return address; } @@ -1612,14 +1614,14 @@ DefineIndex(ParseState *pstate, table_close(rel, NoLock); /* - * If this is the top-level index, the command is done overall; - * otherwise, increment progress to report one child index is done. + * Increment progress to report one child index is done. */ - if (!OidIsValid(parentIndexId)) - pgstat_progress_end_command(); - else + if (OidIsValid(parentIndexId)) pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1); + if (progress_started) + pgstat_progress_end_command(); + return address; } @@ -1833,7 +1835,8 @@ DefineIndex(ParseState *pstate, */ UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock); - pgstat_progress_end_command(); + if (progress_started) + pgstat_progress_end_command(); return address; } @@ -2987,7 +2990,6 @@ ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLev { ReindexParams newparams = *params; - newparams.options |= REINDEXOPT_REPORT_PROGRESS; reindex_index(stmt, indOid, false, persistence, &newparams); } } @@ -3110,7 +3112,6 @@ ReindexTable(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLev { ReindexParams newparams = *params; - newparams.options |= REINDEXOPT_REPORT_PROGRESS; result = reindex_relation(stmt, heapOid, REINDEX_REL_PROCESS_TOAST | REINDEX_REL_CHECK_CONSTRAINTS, @@ -3535,8 +3536,7 @@ ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids, const Reind { ReindexParams newparams = *params; - newparams.options |= - REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK; + newparams.options |= REINDEXOPT_MISSING_OK; reindex_index(stmt, relid, false, relpersistence, &newparams); PopActiveSnapshot(); /* reindex_index() does the verbose output */ @@ -3546,8 +3546,7 @@ ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids, const Reind bool result; ReindexParams newparams = *params; - newparams.options |= - REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK; + newparams.options |= REINDEXOPT_MISSING_OK; result = reindex_relation(stmt, relid, REINDEX_REL_PROCESS_TOAST | REINDEX_REL_CHECK_CONSTRAINTS, @@ -3967,7 +3966,6 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein elog(ERROR, "cannot reindex a temporary table concurrently"); pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, idx->tableId); - progress_vals[0] = PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY; progress_vals[1] = 0; /* initializing */ progress_vals[2] = idx->indexId; @@ -3991,11 +3989,11 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein /* Create new index definition based on given index */ newIndexId = index_create_copy(heapRel, INDEX_CREATE_CONCURRENT | - INDEX_CREATE_SKIP_BUILD | - INDEX_CREATE_SUPPRESS_PROGRESS, + INDEX_CREATE_SKIP_BUILD, idx->indexId, tablespaceid, concurrentName); + pgstat_progress_end_command(); /* * Now open the relation of the new index, a session-level lock is @@ -4114,9 +4112,11 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein * DefineIndex() for more details. */ + pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, relationOid); pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_1); WaitForLockersMultiple(lockTags, ShareLock, true); + pgstat_progress_end_command(); CommitTransactionCommand(); foreach(lc, newIndexIds) @@ -4153,6 +4153,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein /* Perform concurrent build of new index */ index_concurrently_build(newidx->tableId, newidx->indexId); + pgstat_progress_end_command(); PopActiveSnapshot(); CommitTransactionCommand(); @@ -4173,9 +4174,11 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein * for more details. */ + pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, relationOid); pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_2); WaitForLockersMultiple(lockTags, ShareLock, true); + pgstat_progress_end_command(); CommitTransactionCommand(); foreach(lc, newIndexIds) @@ -4246,6 +4249,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_3); WaitForOlderSnapshots(limitXmin, true); + pgstat_progress_end_command(); CommitTransactionCommand(); } @@ -4341,6 +4345,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein */ INJECTION_POINT("reindex-relation-concurrently-before-set-dead", NULL); + pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, relationOid); pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_4); WaitForLockersMultiple(lockTags, AccessExclusiveLock, true); @@ -4386,6 +4391,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_5); WaitForLockersMultiple(lockTags, AccessExclusiveLock, true); + pgstat_progress_end_command(); PushActiveSnapshot(GetTransactionSnapshot()); @@ -4461,8 +4467,6 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein MemoryContextDelete(private_context); - pgstat_progress_end_command(); - return true; } diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 4d177c868bb..51078d10202 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1920,11 +1920,19 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, { ObjectAddress object; Oid mapped_tables[4]; + bool is_repack; int i; + /* + * XXX Shouldn't we add an argument indicating that the function is being + * called from REPACK? + */ + is_repack = MyBEEntry->st_progress_command == PROGRESS_COMMAND_REPACK; + /* Report that we are now swapping relation files */ - pgstat_progress_update_param(PROGRESS_REPACK_PHASE, - PROGRESS_REPACK_PHASE_SWAP_REL_FILES); + if (is_repack) + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_SWAP_REL_FILES); /* Zero out possible results from swapped_relation_files */ memset(mapped_tables, 0, sizeof(mapped_tables)); @@ -1981,15 +1989,17 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT; /* Report that we are now reindexing relations */ - pgstat_progress_update_param(PROGRESS_REPACK_PHASE, - PROGRESS_REPACK_PHASE_REBUILD_INDEX); + if (is_repack) + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_REBUILD_INDEX); reindex_relation(NULL, OIDOldHeap, reindex_flags, &reindex_params); } /* Report that we are now doing clean up */ - pgstat_progress_update_param(PROGRESS_REPACK_PHASE, - PROGRESS_REPACK_PHASE_FINAL_CLEANUP); + if (is_repack) + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_FINAL_CLEANUP); /* * If the relation being rebuilt is pg_class, swap_relation_files() @@ -3348,9 +3358,20 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) "repacknew", get_rel_namespace(ind->rd_index->indrelid), false); - newindex = index_create_copy(NewHeap, INDEX_CREATE_SUPPRESS_PROGRESS, + + /* + * We build the index on the new heap, but after the swap phase it'll + * become an index on the old heap. It makes more sense to report the + * progress this way. (The reporting API expects that both command and + * subcommand deal with the same target.) + */ + pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, + RelationGetRelid(OldHeap)); + newindex = index_create_copy(NewHeap, 0, oldindex, ind->rd_rel->reltablespace, newName); + pgstat_progress_end_command(); + copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap)); result = lappend_oid(result, newindex); diff --git a/src/backend/utils/activity/backend_progress.c b/src/backend/utils/activity/backend_progress.c index 6d2049105ab..ce7ca533627 100644 --- a/src/backend/utils/activity/backend_progress.c +++ b/src/backend/utils/activity/backend_progress.c @@ -22,6 +22,10 @@ * * Set st_progress_command (and st_progress_command_target) in own backend * entry. Also, zero-initialize st_progress_param array. + * + * If command has already been started, start a sub-command. Only parameters + * of the sub-command are updated until pgstat_progress_end_command() is + * called. (Target relation must be the same for both commands.) *----------- */ void @@ -33,9 +37,30 @@ pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid) return; PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - beentry->st_progress_command = cmdtype; - beentry->st_progress_command_target = relid; - MemSet(&beentry->st_progress_param, 0, sizeof(beentry->st_progress_param)); + /* Sub-command should not be started w/o parent command. */ + if (beentry->st_progress_command == PROGRESS_COMMAND_INVALID) + { + Assert(beentry->st_progress_command2 == PROGRESS_COMMAND_INVALID); + + beentry->st_progress_command = cmdtype; + beentry->st_progress_command_target = relid; + MemSet(&beentry->st_progress_param, 0, + sizeof(beentry->st_progress_param)); + } + else if (beentry->st_progress_command2 == PROGRESS_COMMAND_INVALID) + { + Assert(beentry->st_progress_command != PROGRESS_COMMAND_INVALID); + + beentry->st_progress_command2 = cmdtype; + beentry->st_progress_command_target2 = relid; + MemSet(&beentry->st_progress_param2, 0, + sizeof(beentry->st_progress_param2)); + } + else + { + /* Only one level of nesting is supported. */ + Assert(false); + } PGSTAT_END_WRITE_ACTIVITY(beentry); } @@ -49,14 +74,21 @@ void pgstat_progress_update_param(int index, int64 val) { volatile PgBackendStatus *beentry = MyBEEntry; + volatile int64 *params; Assert(index >= 0 && index < PGSTAT_NUM_PROGRESS_PARAM); if (!beentry || !pgstat_track_activities) return; + Assert(beentry->st_progress_command != PROGRESS_COMMAND_INVALID || + beentry->st_progress_command2 != PROGRESS_COMMAND_INVALID); + + params = (beentry->st_progress_command2 == PROGRESS_COMMAND_INVALID) ? + beentry->st_progress_param : beentry->st_progress_param2; + PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - beentry->st_progress_param[index] = val; + params[index] = val; PGSTAT_END_WRITE_ACTIVITY(beentry); } @@ -70,14 +102,21 @@ void pgstat_progress_incr_param(int index, int64 incr) { volatile PgBackendStatus *beentry = MyBEEntry; + volatile int64 *params; Assert(index >= 0 && index < PGSTAT_NUM_PROGRESS_PARAM); if (!beentry || !pgstat_track_activities) return; + Assert(beentry->st_progress_command != PROGRESS_COMMAND_INVALID || + beentry->st_progress_command2 != PROGRESS_COMMAND_INVALID); + + params = (beentry->st_progress_command2 == PROGRESS_COMMAND_INVALID) ? + beentry->st_progress_param : beentry->st_progress_param2; + PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - beentry->st_progress_param[index] += incr; + params[index] += incr; PGSTAT_END_WRITE_ACTIVITY(beentry); } @@ -122,17 +161,24 @@ pgstat_progress_update_multi_param(int nparam, const int *index, { volatile PgBackendStatus *beentry = MyBEEntry; int i; + volatile int64 *params; if (!beentry || !pgstat_track_activities || nparam == 0) return; + Assert(beentry->st_progress_command != PROGRESS_COMMAND_INVALID || + beentry->st_progress_command2 != PROGRESS_COMMAND_INVALID); + + params = (beentry->st_progress_command2 == PROGRESS_COMMAND_INVALID) ? + beentry->st_progress_param : beentry->st_progress_param2; + PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); for (i = 0; i < nparam; ++i) { Assert(index[i] >= 0 && index[i] < PGSTAT_NUM_PROGRESS_PARAM); - beentry->st_progress_param[index[i]] = val[i]; + params[index[i]] = val[i]; } PGSTAT_END_WRITE_ACTIVITY(beentry); @@ -142,7 +188,7 @@ pgstat_progress_update_multi_param(int nparam, const int *index, * pgstat_progress_end_command() - * * Reset st_progress_command (and st_progress_command_target) in own backend - * entry. This signals the end of the command. + * entry. This signals the end of the command (or a sub-command). *----------- */ void @@ -153,11 +199,20 @@ pgstat_progress_end_command(void) if (!beentry || !pgstat_track_activities) return; - if (beentry->st_progress_command == PROGRESS_COMMAND_INVALID) - return; - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - beentry->st_progress_command = PROGRESS_COMMAND_INVALID; - beentry->st_progress_command_target = InvalidOid; + + if (beentry->st_progress_command2 != PROGRESS_COMMAND_INVALID) + { + Assert(beentry->st_progress_command != PROGRESS_COMMAND_INVALID); + + beentry->st_progress_command2 = PROGRESS_COMMAND_INVALID; + beentry->st_progress_command_target2 = InvalidOid; + } + else + { + beentry->st_progress_command = PROGRESS_COMMAND_INVALID; + beentry->st_progress_command_target = InvalidOid; + } + PGSTAT_END_WRITE_ACTIVITY(beentry); } diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c index d685fc5cd87..15675992415 100644 --- a/src/backend/utils/activity/backend_status.c +++ b/src/backend/utils/activity/backend_status.c @@ -284,6 +284,7 @@ pgstat_bestart_initial(void) lbeentry.st_state = STATE_STARTING; lbeentry.st_progress_command = PROGRESS_COMMAND_INVALID; + lbeentry.st_progress_command2 = PROGRESS_COMMAND_INVALID; lbeentry.st_progress_command_target = InvalidOid; lbeentry.st_query_id = INT64CONST(0); lbeentry.st_plan_id = INT64CONST(0); diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 6f9c9c72de5..d0513d6931b 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -314,6 +314,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) Datum values[PG_STAT_GET_PROGRESS_COLS] = {0}; bool nulls[PG_STAT_GET_PROGRESS_COLS] = {0}; int i; + volatile int64 *params; local_beentry = pgstat_get_local_beentry_by_index(curr_backend); beentry = &local_beentry->backendStatus; @@ -322,7 +323,11 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) * Report values for only those backends which are running the given * command. */ - if (beentry->st_progress_command != cmdtype) + if (beentry->st_progress_command == cmdtype) + params = beentry->st_progress_param; + else if (beentry->st_progress_command2 == cmdtype) + params = beentry->st_progress_param2; + else continue; /* Value available to all callers */ @@ -332,9 +337,13 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) /* show rest of the values including relid only to role members */ if (HAS_PGSTAT_PERMISSIONS(beentry->st_userid)) { - values[2] = ObjectIdGetDatum(beentry->st_progress_command_target); + if (beentry->st_progress_command == cmdtype) + values[2] = ObjectIdGetDatum(beentry->st_progress_command_target); + else if (beentry->st_progress_command2 == cmdtype) + values[2] = ObjectIdGetDatum(beentry->st_progress_command_target2); + for (i = 0; i < PGSTAT_NUM_PROGRESS_PARAM; i++) - values[i + 3] = Int64GetDatum(beentry->st_progress_param[i]); + values[i + 3] = Int64GetDatum(params[i]); } else { diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..d4c77f3df3f 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -45,9 +45,8 @@ typedef struct ReindexParams /* flag bits for ReindexParams->flags */ #define REINDEXOPT_VERBOSE 0x01 /* print progress info */ -#define REINDEXOPT_REPORT_PROGRESS 0x02 /* report pgstat progress */ -#define REINDEXOPT_MISSING_OK 0x04 /* skip missing relations */ -#define REINDEXOPT_CONCURRENTLY 0x08 /* concurrent mode */ +#define REINDEXOPT_MISSING_OK 0x02 /* skip missing relations */ +#define REINDEXOPT_CONCURRENTLY 0x04 /* concurrent mode */ /* state info for validate_index bulkdelete callback */ typedef struct ValidateIndexState @@ -71,7 +70,6 @@ extern void index_check_primary_key(Relation heapRel, #define INDEX_CREATE_IF_NOT_EXISTS (1 << 4) #define INDEX_CREATE_PARTITIONED (1 << 5) #define INDEX_CREATE_INVALID (1 << 6) -#define INDEX_CREATE_SUPPRESS_PROGRESS (1 << 7) extern Oid index_create(Relation heapRelation, const char *indexRelationName, @@ -149,8 +147,7 @@ extern void index_build(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, bool isreindex, - bool parallel, - bool progress); + bool parallel); extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot); diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h index a334e096e4a..8b39c59df7b 100644 --- a/src/include/utils/backend_status.h +++ b/src/include/utils/backend_status.h @@ -169,6 +169,15 @@ typedef struct PgBackendStatus Oid st_progress_command_target; int64 st_progress_param[PGSTAT_NUM_PROGRESS_PARAM]; + /* + * Some commands have a sub-command, e.g. REPACK (re)builds indexes. The + * target can be different, e.g. when the sub-command builds an index on + * TOAST relation. + */ + ProgressCommandType st_progress_command2; + Oid st_progress_command_target2; + int64 st_progress_param2[PGSTAT_NUM_PROGRESS_PARAM]; + /* query identifier, optionally computed using post_parse_analyze_hook */ int64 st_query_id; @@ -176,6 +185,16 @@ typedef struct PgBackendStatus int64 st_plan_id; } PgBackendStatus; +/* + * Check the depth of progress tracking. + * + * 0 - tracking disabled + * 1 - enabled for the "main" command + * 2 - enabled for both main command and its sub-command. + */ +#define PGSTAT_PROGRESS_STATE(state) \ + ((state)->st_progress_command == PROGRESS_COMMAND_INVALID ? 0 :\ + (state)->st_progress_command2 == PROGRESS_COMMAND_INVALID ? 1 : 2) /* * Macros to load and store st_changecount with appropriate memory barriers. -- 2.52.0
