v34-0002-Log-plan-along-with-instrumentation-details.patch
application/octet-stream
Filename: v34-0002-Log-plan-along-with-instrumentation-details.patch
Type: application/octet-stream
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v34-0002
Subject: Log plan along with instrumentation details
| File | + | − |
|---|---|---|
| src/backend/commands/explain.c | 101 | 30 |
| src/backend/executor/instrument.c | 18 | 2 |
| src/include/executor/instrument.h | 1 | 0 |
From fc656131a2920a8c8a583d749cb7c18c3c57fbfa Mon Sep 17 00:00:00 2001
From: Rafael Castro <rafaelthca@gmail.com>
Date: Wed, 6 Dec 2023 19:18:32 -0300
Subject: [PATCH v34 2/2] Log plan along with instrumentation details
Instrumentation is enabled when using EXPLAIN ANALYZE or with auto_explain
and the proper settings.
A plan output with instrumentation also contains details for nodes that
weren't executed yet, depicted with (never executed). This incremental
patch also includes a new visualization, depicted with (in progress)
that denotes nodes currently being executed, ie, function InstrStartNode
was called but InstrStopNode not yet.
---
src/backend/commands/explain.c | 131 +++++++++++++++++++++++-------
src/backend/executor/instrument.c | 20 ++++-
src/include/executor/instrument.h | 1 +
3 files changed, 120 insertions(+), 32 deletions(-)
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a3c0a6a3d9..7369a75cf7 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1066,8 +1066,17 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
char *relname;
char *conname = NULL;
- /* Must clean up instrumentation state */
- InstrEndLoop(instr);
+ /* Signaled explain, clone instrumentation */
+ if (es->signaled) {
+ instr = palloc(sizeof(*instr));
+ *instr = *(rInfo->ri_TrigInstrument + nt);
+ /* Force end loop even if node is in progress */
+ InstrEndLoopForce(instr);
+ }
+ else {
+ instr = rInfo->ri_TrigInstrument + nt;
+ InstrEndLoop(instr);
+ }
/*
* We ignore triggers that were never invoked; they likely aren't
@@ -1230,6 +1239,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
const char *operation = NULL;
const char *custom_name = NULL;
ExplainWorkersState *save_workers_state = es->workers_state;
+ Instrumentation *local_instr = NULL;
int save_indent = es->indent;
bool haschildren;
@@ -1682,37 +1692,63 @@ ExplainNode(PlanState *planstate, List *ancestors,
/*
* We have to forcibly clean up the instrumentation state because we
* haven't done ExecutorEnd yet. This is pretty grotty ...
- * This cleanup should not be done when the query has already been
- * executed and explain has been called by signal, as the target query
- * may use instrumentation and clean itself up.
*
* Note: contrib/auto_explain could cause instrumentation to be set up
* even though we didn't ask for it here. Be careful not to print any
* instrumentation results the user didn't ask for. But we do the
* InstrEndLoop call anyway, if possible, to reduce the number of cases
* auto_explain has to contend with.
+ *
+ * For regular explains instrumentation clean up is called directly in
+ * the main instrumentation objects. Signaled plan logging needs to clone
+ * instrumentation object and forcibly end the loop in nodes that may
+ * be running.
*/
- if (planstate->instrument && !es->signaled)
- InstrEndLoop(planstate->instrument);
+ if (planstate->instrument) {
+ /* In flight explain. Clone instrumentation */
+ if (es->signaled) {
+ local_instr = palloc(sizeof(*local_instr));
+ *local_instr = *planstate->instrument;
+ /* Force end loop even if node is in progress */
+ InstrEndLoopForce(local_instr);
+ }
+ /* Use main instrumentation */
+ else {
+ local_instr = planstate->instrument;
+ InstrEndLoop(local_instr);
+ }
+ }
if (es->analyze &&
- planstate->instrument && planstate->instrument->nloops > 0)
+ local_instr && local_instr->nloops > 0)
{
- double nloops = planstate->instrument->nloops;
- double startup_ms = 1000.0 * planstate->instrument->startup / nloops;
- double total_ms = 1000.0 * planstate->instrument->total / nloops;
- double rows = planstate->instrument->ntuples / nloops;
+ double nloops = local_instr->nloops;
+ double startup_ms = 1000.0 * local_instr->startup / nloops;
+ double total_ms = 1000.0 * local_instr->total / nloops;
+ double rows = local_instr->ntuples / nloops;
if (es->format == EXPLAIN_FORMAT_TEXT)
{
if (es->timing)
- appendStringInfo(es->str,
+ /* Node in progress */
+ if (!INSTR_TIME_IS_ZERO(planstate->instrument->starttime))
+ appendStringInfo(es->str,
+ " (actual time=%.3f..%.3f rows=%.0f loops=%.0f) (in progress)",
+ startup_ms, total_ms, rows, nloops);
+ else
+ appendStringInfo(es->str,
" (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
startup_ms, total_ms, rows, nloops);
else
- appendStringInfo(es->str,
- " (actual rows=%.0f loops=%.0f)",
- rows, nloops);
+ /* Node in progress */
+ if (!INSTR_TIME_IS_ZERO(planstate->instrument->starttime))
+ appendStringInfo(es->str,
+ " (actual rows=%.0f loops=%.0f) (in progress)",
+ rows, nloops);
+ else
+ appendStringInfo(es->str,
+ " (actual rows=%.0f loops=%.0f)",
+ rows, nloops);
}
else
{
@@ -1848,7 +1884,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
if (es->analyze)
ExplainPropertyFloat("Heap Fetches", NULL,
- planstate->instrument->ntuples2, 0, es);
+ local_instr->ntuples2, 0, es);
break;
case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
@@ -2143,10 +2179,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
}
/* Show buffer/WAL usage */
- if (es->buffers && planstate->instrument)
- show_buffer_usage(es, &planstate->instrument->bufusage, false);
- if (es->wal && planstate->instrument)
- show_wal_usage(es, &planstate->instrument->walusage);
+ if (es->buffers && local_instr)
+ show_buffer_usage(es, &local_instr->bufusage, false);
+ if (es->wal && local_instr)
+ show_wal_usage(es, &local_instr->walusage);
/* Prepare per-worker buffer/WAL usage */
if (es->workers_state && (es->buffers || es->wal) && es->verbose)
@@ -4127,11 +4163,22 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
double total;
double insert_path;
double other_path;
-
- InstrEndLoop(outerPlanState(mtstate)->instrument);
+ Instrumentation *local_instr;
+
+ /* Signaled explain, clone instrumentation */
+ if (es->signaled) {
+ local_instr = palloc(sizeof(*local_instr));
+ *local_instr = *outerPlanState(mtstate)->instrument;
+ /* Force end loop even if node is in progress */
+ InstrEndLoopForce(local_instr);
+ }
+ else {
+ local_instr = outerPlanState(mtstate)->instrument;
+ InstrEndLoop(local_instr);
+ }
/* count the number of source rows */
- total = outerPlanState(mtstate)->instrument->ntuples;
+ total = local_instr->ntuples;
other_path = mtstate->ps.instrument->ntuples2;
insert_path = total - other_path;
@@ -4143,7 +4190,7 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
}
else if (node->operation == CMD_MERGE)
{
- /* EXPLAIN ANALYZE display of tuples processed */
+ /* Signaled explain, clone instrumentation */
if (es->analyze && mtstate->ps.instrument)
{
double total;
@@ -4151,11 +4198,22 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
double update_path;
double delete_path;
double skipped_path;
-
- InstrEndLoop(outerPlanState(mtstate)->instrument);
+ Instrumentation *local_instr;
+
+ /* In flight explain. Clone instrumentation */
+ if (es->signaled) {
+ local_instr = palloc(sizeof(*local_instr));
+ *local_instr = *outerPlanState(mtstate)->instrument;
+ /* Force end loop even if node is in progress */
+ InstrEndLoopForce(local_instr);
+ }
+ else {
+ local_instr = outerPlanState(mtstate)->instrument;
+ InstrEndLoop(local_instr);
+ }
/* count the number of source rows */
- total = outerPlanState(mtstate)->instrument->ntuples;
+ total = local_instr->ntuples;
insert_path = mtstate->mt_merge_inserted;
update_path = mtstate->mt_merge_updated;
delete_path = mtstate->mt_merge_deleted;
@@ -5228,6 +5286,18 @@ ProcessLogQueryPlanInterrupt(void)
es->verbose = true;
es->signaled = true;
+ /*
+ * Adjust instrumentation related settings based
+ * on QueryDesc object.
+ */
+ es->analyze = ActiveQueryDesc->instrument_options;
+ es->buffers = (ActiveQueryDesc->instrument_options &
+ INSTRUMENT_BUFFERS) != 0;
+ es->wal = (ActiveQueryDesc->instrument_options &
+ INSTRUMENT_WAL) != 0;
+ es->timing = (ActiveQueryDesc->instrument_options &
+ INSTRUMENT_TIMER) != 0;
+
ExplainAssembleLogOutput(es, ActiveQueryDesc, EXPLAIN_FORMAT_TEXT, 0, -1);
ereport(LOG_SERVER_ONLY,
@@ -5272,10 +5342,11 @@ pg_log_query_plan(PG_FUNCTION_ARGS)
}
be_status = pgstat_get_beentry_by_backend_id(proc->backendId);
- if (be_status->st_backendType != B_BACKEND)
+ if (be_status->st_backendType != B_BACKEND &&
+ be_status->st_backendType != B_BG_WORKER)
{
ereport(WARNING,
- (errmsg("PID %d is not a PostgreSQL client backend process", pid)));
+ (errmsg("PID %d is not a PostgreSQL client backend or background worker process", pid)));
PG_RETURN_BOOL(false);
}
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index c383f34c06..7000e4d8bc 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -25,6 +25,8 @@ static WalUsage save_pgWalUsage;
static void BufferUsageAdd(BufferUsage *dst, const BufferUsage *add);
static void WalUsageAdd(WalUsage *dst, WalUsage *add);
+static void InstrEndLoopInternal(Instrumentation *instr, bool force);
+
/* Allocate new instrumentation structure(s) */
Instrumentation *
@@ -137,7 +139,7 @@ InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
/* Finish a run cycle for a plan node */
void
-InstrEndLoop(Instrumentation *instr)
+InstrEndLoopInternal(Instrumentation *instr, bool force)
{
double totaltime;
@@ -145,7 +147,7 @@ InstrEndLoop(Instrumentation *instr)
if (!instr->running)
return;
- if (!INSTR_TIME_IS_ZERO(instr->starttime))
+ if (!INSTR_TIME_IS_ZERO(instr->starttime) && !force)
elog(ERROR, "InstrEndLoop called on running node");
/* Accumulate per-cycle statistics into totals */
@@ -164,6 +166,20 @@ InstrEndLoop(Instrumentation *instr)
instr->tuplecount = 0;
}
+/* Safely finish a run cycle for a plan node */
+void
+InstrEndLoop(Instrumentation *instr)
+{
+ InstrEndLoopInternal(instr, false);
+}
+
+/* Forcibly finish a run cycle for a plan node */
+void
+InstrEndLoopForce(Instrumentation *instr)
+{
+ InstrEndLoopInternal(instr, true);
+}
+
/* aggregate instrumentation information */
void
InstrAggNode(Instrumentation *dst, Instrumentation *add)
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index d5d69941c5..b7c98dfd78 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -108,6 +108,7 @@ extern void InstrStartNode(Instrumentation *instr);
extern void InstrStopNode(Instrumentation *instr, double nTuples);
extern void InstrUpdateTupleCount(Instrumentation *instr, double nTuples);
extern void InstrEndLoop(Instrumentation *instr);
+extern void InstrEndLoopForce(Instrumentation *instr);
extern void InstrAggNode(Instrumentation *dst, Instrumentation *add);
extern void InstrStartParallelQuery(void);
extern void InstrEndParallelQuery(BufferUsage *bufusage, WalUsage *walusage);
--
2.32.0 (Apple Git-132)