From: Tomas Vondra <tomas.vondra@2ndquadrant.com>
To: Alvaro Herrera <alvherre@2ndquadrant.com>
Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
Cc: tsunakawa.takay@fujitsu.com <tsunakawa.takay@fujitsu.com>
Cc: pgsql-hackers@postgresql.org <pgsql-hackers@postgresql.org>
Subject: Re: SLRU statistics
Date: Sat, 28 Mar 2020 01:26:30 +0100
Message-ID: <20200328002630.2nz5y3dvymqnvxtv@development> (raw)
In-Reply-To: <20200327004111.md4suoi6u5suv2qe@development>
References: <20200229122441.ow7gnnqgesmojyc3@development>
<20200229144426.GA32500@alvherre.pgsql>
<20200229205518.xrxo535zgd7cl6no@development>
<20200327004111.md4suoi6u5suv2qe@development>
Hi,
here is a bit improved version of the patch - I've been annoyed by how
the resetting works (per-entry timestamp, but resetting all entries) so
I've added a new function pg_stat_reset_slru() that allows resetting
either all entries or just one entry (identified by name). So
SELECT pg_stat_reset_slru('clog');
resets just "clog" SLRU counters, while
SELECT pg_stat_reset_slru(NULL);
resets all entries.
I've also done a bit of benchmarking, to see if this has measurable
impact (in which case it might deserve a new GUC), and I think it's not
measurable. I've used a tiny unlogged table (single row).
CREATE UNLOGGED TABLE t (a int);
INSERT INTO t VALUES (1);
and then short pgbench runs with a single client, updatint the row. I've
been unable to measure any regression, it's all well within 1% so noise.
But perhaps there's some other benchmark that I should do?
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From 1a065c21a9a791909cd1ca752db5aaf1f814fe37 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tv@fuzzy.cz>
Date: Thu, 26 Mar 2020 20:52:26 +0100
Subject: [PATCH] Collect SLRU statistics
Adds a new system view pg_stats_slru with stats about SLRU caches, and
a function pg_stat_reset_slru() to reset either all counters or just
counters for a single SLRU.
There is no SLRU registry this patch could use, so it simply uses the
SLRU name (which is also used for LWLock tranche name) as an identifier,
and a predefined list of SLRU names, and an extra "others" entry for
SLRUs without a dedicated entry. Presumably, the number of extensions
defining their own SLRU is very small.
Author: Tomas Vondra
Reviewed-by: Alvaro Herrera
Discussion: https://www.postgresql.org/message-id/flat/20200119143707.gyinppnigokesjok@development
---
doc/src/sgml/monitoring.sgml | 97 +++++++++
src/backend/access/transam/slru.c | 23 ++
src/backend/catalog/system_views.sql | 14 ++
src/backend/postmaster/pgstat.c | 300 +++++++++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 91 ++++++++
src/include/catalog/pg_proc.dat | 15 ++
src/include/pgstat.h | 65 ++++++
src/test/regress/expected/rules.out | 10 +
8 files changed, 615 insertions(+)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgmlindex 270178d57e..7ba0dbee6a 100644--- a/doc/src/sgml/monitoring.sgml+++ b/doc/src/sgml/monitoring.sgml@@ -575,6 +575,13 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
yet included in <structname>pg_stat_user_functions</structname>).</entry>
</row>
+ <row>+ <entry><structname>pg_stat_slru</structname><indexterm><primary>pg_stat_slru</primary></indexterm></entry>+ <entry>One row per SLRU, showing statistics of operations. See+ <xref linkend="pg-stat-slru-view"/> for details.+ </entry>+ </row>+
</tbody>
</tgroup>
</table>
@@ -3254,6 +3261,76 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
</tgroup>
</table>
+ <para>+ The <structname>pg_stat_slru</structname> view will contain+ one row for each tracked SLRU cache, showing statistics about access+ to cached pages.+ </para>++ <table id="pg-stat-slru-view" xreflabel="pg_stat_slru">+ <title><structname>pg_stat_slru</structname> View</title>+ <tgroup cols="3">+ <thead>+ <row>+ <entry>Column</entry>+ <entry>Type</entry>+ <entry>Description</entry>+ </row>+ </thead>++ <tbody>+ <row>+ <entry><structfield>name</structfield></entry>+ <entry><type>name</type></entry>+ <entry>name of the SLRU</entry>+ </row>+ <row>+ <entry><structfield>blks_zeroed</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of blocks zeroed during initializations</entry>+ </row>+ <row>+ <entry><structfield>blks_hit</structfield></entry>+ <entry><type>biging</type></entry>+ <entry>Number of times disk blocks were found already in the SLRU,+ so that a read was not necessary (this only includes hits in the+ SLRU, not the operating system's file system cache)+ </entry>+ </row>+ <row>+ <entry><structfield>blks_read</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of disk blocks read for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>blks_written</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of disk blocks written for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>blks_exists</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of blocks checked for existence for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>flushes</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of flushes of dirty data for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>truncates</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of truncates for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>stats_reset</structfield></entry>+ <entry><type>timestamp with time zone</type></entry>+ <entry>Time at which these statistics were last reset</entry>+ </row>+ </tbody>+ </tgroup>+ </table>+
<para>
The <structname>pg_stat_user_functions</structname> view will contain
one row for each tracked function, showing statistics about executions of
@@ -3378,6 +3455,26 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
function can be granted to others)
</entry>
</row>
++ <row>+ <entry><literal><function>pg_stat_reset_slru</function>(text)</literal><indexterm><primary>pg_stat_reset_slru</primary></indexterm></entry>+ <entry><type>void</type></entry>+ <entry>+ Reset statistics either for a single SLRU or all SLRUs in the cluster+ to zero (requires superuser privileges by default, but EXECUTE for this+ function can be granted to others).+ Calling <literal>pg_stat_reset_slru(NULL)</literal> will zero all the+ counters shown in the <structname>pg_stat_slru</structname> view for+ all SLRU caches.+ Calling <literal>pg_stat_reset_slru(name)</literal> with names from a+ predefined list (<literal>async</literal>, <literal>clog</literal>,+ <literal>commit_timestamp</literal>, <literal>multixact_offset</literal>,+ <literal>multixact_member</literal>, <literal>oldserxid</literal>,+ <literal>pg_xact</literal>, <literal>subtrans</literal> and+ <literal>other</literal>) resets counters for only that entry.+ Names not included in this list are treated as <literal>other</literal>.+ </entry>+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.cindex d5b7a08f73..f7160dd574 100644--- a/src/backend/access/transam/slru.c+++ b/src/backend/access/transam/slru.c@@ -286,6 +286,9 @@ SimpleLruZeroPage(SlruCtl ctl, int pageno)
/* Assume this page is now the latest active page */
shared->latest_page_number = pageno;
+ /* update the stats counter of zeroed pages */+ pgstat_slru_count_page_zeroed(ctl);+
return slotno;
}
@@ -403,6 +406,10 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
}
/* Otherwise, it's ready to use */
SlruRecentlyUsed(shared, slotno);
++ /* update the stats counter of pages found in the SLRU */+ pgstat_slru_count_page_hit(ctl);+
return slotno;
}
@@ -444,6 +451,10 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
SlruReportIOError(ctl, pageno, xid);
SlruRecentlyUsed(shared, slotno);
++ /* update the stats counter of pages not found in SLRU */+ pgstat_slru_count_page_read(ctl);+
return slotno;
}
}
@@ -596,6 +607,9 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
bool result;
off_t endpos;
+ /* update the stats counter of checked pages */+ pgstat_slru_count_page_exists(ctl);+
SlruFileName(ctl, path, segno);
fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
@@ -730,6 +744,9 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruFlush fdata)
char path[MAXPGPATH];
int fd = -1;
+ /* update the stats counter of written pages */+ pgstat_slru_count_page_written(ctl);+
/*
* Honor the write-WAL-before-data rule, if appropriate, so that we do not
* write out data before associated WAL records. This is the same action
@@ -1125,6 +1142,9 @@ SimpleLruFlush(SlruCtl ctl, bool allow_redirtied)
int i;
bool ok;
+ /* update the stats counter of flushes */+ pgstat_slru_count_flush(ctl);+
/*
* Find and write dirty pages
*/
@@ -1186,6 +1206,9 @@ SimpleLruTruncate(SlruCtl ctl, int cutoffPage)
SlruShared shared = ctl->shared;
int slotno;
+ /* update the stats counter of truncates */+ pgstat_slru_count_truncate(ctl);+
/*
* The cutoff point is the start of the segment containing cutoffPage.
*/
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sqlindex 5a6dc61630..09e226f34d 100644--- a/src/backend/catalog/system_views.sql+++ b/src/backend/catalog/system_views.sql@@ -792,6 +792,19 @@ CREATE VIEW pg_stat_replication AS
JOIN pg_stat_get_wal_senders() AS W ON (S.pid = W.pid)
LEFT JOIN pg_authid AS U ON (S.usesysid = U.oid);
+CREATE VIEW pg_stat_slru AS+ SELECT+ s.name,+ s.blks_zeroed,+ s.blks_hit,+ s.blks_read,+ s.blks_written,+ s.blks_exists,+ s.flushes,+ s.truncates,+ s.stats_reset+ FROM pg_stat_get_slru() s;+
CREATE VIEW pg_stat_wal_receiver AS
SELECT
s.pid,
@@ -1409,6 +1422,7 @@ REVOKE EXECUTE ON FUNCTION pg_promote(boolean, integer) FROM public;
REVOKE EXECUTE ON FUNCTION pg_stat_reset() FROM public;
REVOKE EXECUTE ON FUNCTION pg_stat_reset_shared(text) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_stat_reset_slru(text) FROM public;
REVOKE EXECUTE ON FUNCTION pg_stat_reset_single_table_counters(oid) FROM public;
REVOKE EXECUTE ON FUNCTION pg_stat_reset_single_function_counters(oid) FROM public;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.cindex 4763c24be9..ea0fdade58 100644--- a/src/backend/postmaster/pgstat.c+++ b/src/backend/postmaster/pgstat.c@@ -141,6 +141,24 @@ char *pgstat_stat_tmpname = NULL;
*/
PgStat_MsgBgWriter BgWriterStats;
+/*+ * SLRU statistics counters (unused in other processes) stored directly in+ * stats structure so it can be sent without needing to copy things around.+ * We assume this inits to zeroes.+ *+ * There's a separte entry for each SLRU we have. The "other" entry is used+ * for all SLRUs without an explicit entry (e.g. SLRUs in extensions).+ */+static char *slru_names[] = {"async", "clog", "commit_timestamp",+ "multixact_offset", "multixact_member",+ "oldserxid", "pg_xact", "subtrans",+ "other" /* has to be last */};++#define SLRU_NUM_ELEMENTS (sizeof(slru_names) / sizeof(char *))++/* entries in the same order as slru_names */+PgStat_MsgSLRU SLRUStats[SLRU_NUM_ELEMENTS];+
/* ----------
* Local data
* ----------
@@ -255,6 +273,7 @@ static int localNumBackends = 0;
*/
static PgStat_ArchiverStats archiverStats;
static PgStat_GlobalStats globalStats;
+static PgStat_SLRUStats slruStats[SLRU_NUM_ELEMENTS];
/*
* List of OIDs of databases we need to write out. If an entry is InvalidOid,
@@ -297,6 +316,7 @@ static bool pgstat_db_requested(Oid databaseid);
static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg);
static void pgstat_send_funcstats(void);
+static void pgstat_send_slru(void);
static HTAB *pgstat_collect_oids(Oid catalogid, AttrNumber anum_oid);
static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared);
@@ -319,11 +339,13 @@ static void pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len);
static void pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len);
static void pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len);
static void pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len);
+static void pgstat_recv_resetslrucounter(PgStat_MsgResetslrucounter *msg, int len);
static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len);
static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len);
static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len);
static void pgstat_recv_archiver(PgStat_MsgArchiver *msg, int len);
static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len);
+static void pgstat_recv_slru(PgStat_MsgSLRU *msg, int len);
static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len);
static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
@@ -907,6 +929,9 @@ pgstat_report_stat(bool force)
/* Now, send function statistics */
pgstat_send_funcstats();
++ /* Finally send SLRU statistics */+ pgstat_send_slru();
}
/*
@@ -1372,6 +1397,30 @@ pgstat_reset_single_counter(Oid objoid, PgStat_Single_Reset_Type type)
pgstat_send(&msg, sizeof(msg));
}
+/* ----------+ * pgstat_reset_slru_counter() -+ *+ * Tell the statistics collector to reset a single SLRU counter, or all+ * SLRU counters (when name is null).+ *+ * Permission checking for this function is managed through the normal+ * GRANT system.+ * ----------+ */+void+pgstat_reset_slru_counter(const char *name)+{+ PgStat_MsgResetslrucounter msg;++ if (pgStatSock == PGINVALID_SOCKET)+ return;++ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSLRUCOUNTER);+ msg.m_index = (name) ? pgstat_slru_index(name) : -1;++ pgstat_send(&msg, sizeof(msg));+}+
/* ----------
* pgstat_report_autovac() -
*
@@ -2622,6 +2671,23 @@ pgstat_fetch_global(void)
}
+/*+ * ---------+ * pgstat_fetch_slru() -+ *+ * Support function for the SQL-callable pgstat* functions. Returns+ * a pointer to the slru statistics struct.+ * ---------+ */+PgStat_SLRUStats *+pgstat_fetch_slru(void)+{+ backend_read_statsfile();++ return slruStats;+}++
/* ------------------------------------------------------------
* Functions for management of the shared-memory PgBackendStatus array
* ------------------------------------------------------------
@@ -4325,6 +4391,46 @@ pgstat_send_bgwriter(void)
MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
}
+/* ----------+ * pgstat_send_slru() -+ *+ * Send SLRU statistics to the collector+ * ----------+ */+static void+pgstat_send_slru(void)+{+ int i;++ /* We assume this initializes to zeroes */+ static const PgStat_MsgSLRU all_zeroes;++ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ {+ /*+ * This function can be called even if nothing at all has happened. In+ * this case, avoid sending a completely empty message to the stats+ * collector.+ */+ if (memcmp(&SLRUStats[i], &all_zeroes, sizeof(PgStat_MsgSLRU)) == 0)+ continue;++ /* set the SLRU type before each send */+ SLRUStats[i].m_index = i;++ /*+ * Prepare and send the message+ */+ pgstat_setheader(&SLRUStats[i].m_hdr, PGSTAT_MTYPE_SLRU);+ pgstat_send(&SLRUStats[i], sizeof(PgStat_MsgSLRU));++ /*+ * Clear out the statistics buffer, so it can be re-used.+ */+ MemSet(&SLRUStats[i], 0, sizeof(PgStat_MsgSLRU));+ }+}+
/* ----------
* PgstatCollectorMain() -
@@ -4493,6 +4599,11 @@ PgstatCollectorMain(int argc, char *argv[])
len);
break;
+ case PGSTAT_MTYPE_RESETSLRUCOUNTER:+ pgstat_recv_resetslrucounter(&msg.msg_resetslrucounter,+ len);+ break;+
case PGSTAT_MTYPE_AUTOVAC_START:
pgstat_recv_autovac(&msg.msg_autovacuum_start, len);
break;
@@ -4513,6 +4624,10 @@ PgstatCollectorMain(int argc, char *argv[])
pgstat_recv_bgwriter(&msg.msg_bgwriter, len);
break;
+ case PGSTAT_MTYPE_SLRU:+ pgstat_recv_slru(&msg.msg_slru, len);+ break;+
case PGSTAT_MTYPE_FUNCSTAT:
pgstat_recv_funcstat(&msg.msg_funcstat, len);
break;
@@ -4781,6 +4896,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
rc = fwrite(&archiverStats, sizeof(archiverStats), 1, fpout);
(void) rc; /* we'll check for error with ferror */
+ /*+ * Write SLRU stats struct+ */+ rc = fwrite(slruStats, sizeof(slruStats), 1, fpout);+ (void) rc; /* we'll check for error with ferror */+
/*
* Walk through the database table.
*/
@@ -5016,6 +5137,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
int32 format_id;
bool found;
const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
+ int i;
/*
* The tables will live in pgStatLocalContext.
@@ -5038,6 +5160,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
*/
memset(&globalStats, 0, sizeof(globalStats));
memset(&archiverStats, 0, sizeof(archiverStats));
+ memset(&slruStats, 0, sizeof(slruStats));
/*
* Set the current timestamp (will be kept only in case we can't load an
@@ -5046,6 +5169,13 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
globalStats.stat_reset_timestamp = GetCurrentTimestamp();
archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+ /*+ * Set the same reset timestamp for all SLRU items (one+ * day we might allow resetting individual SLRUs).+ */+ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ slruStats[i].stat_reset_timestamp = globalStats.stat_reset_timestamp;+
/*
* Try to open the stats file. If it doesn't exist, the backends simply
* return zero for anything and the collector simply starts from scratch
@@ -5108,6 +5238,17 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
goto done;
}
+ /*+ * Read SLRU stats struct+ */+ if (fread(slruStats, 1, sizeof(slruStats), fpin) != sizeof(slruStats))+ {+ ereport(pgStatRunningInCollector ? LOG : WARNING,+ (errmsg("corrupted statistics file \"%s\"", statfile)));+ memset(&slruStats, 0, sizeof(slruStats));+ goto done;+ }+
/*
* We found an existing collector stats file. Read it and put all the
* hashtable entries into place.
@@ -5406,9 +5547,11 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
PgStat_StatDBEntry dbentry;
PgStat_GlobalStats myGlobalStats;
PgStat_ArchiverStats myArchiverStats;
+ PgStat_SLRUStats mySLRUStats;
FILE *fpin;
int32 format_id;
const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
+ int i;
/*
* Try to open the stats file. As above, anything but ENOENT is worthy of
@@ -5460,6 +5603,21 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
return false;
}
+ /*+ * Read SLRU stats struct+ */+ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ {+ if (fread(&mySLRUStats, 1, sizeof(PgStat_SLRUStats),+ fpin) != sizeof(PgStat_SLRUStats))+ {+ ereport(pgStatRunningInCollector ? LOG : WARNING,+ (errmsg("corrupted statistics file \"%s\"", statfile)));+ FreeFile(fpin);+ return false;+ }+ }+
/* By default, we're going to return the timestamp of the global file. */
*ts = myGlobalStats.stats_timestamp;
@@ -6057,6 +6215,32 @@ pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len)
HASH_REMOVE, NULL);
}
+/* ----------+ * pgstat_recv_resetslrucounter() -+ *+ * Reset some SLRU statistics of the cluster.+ * ----------+ */+static void+pgstat_recv_resetslrucounter(PgStat_MsgResetslrucounter *msg, int len)+{+ int i;+ TimestampTz ts = GetCurrentTimestamp();++ memset(&slruStats, 0, sizeof(slruStats));++ elog(LOG, "msg->m_index = %d", msg->m_index);++ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ {+ if ((msg->m_index == -1) || (msg->m_index == i))+ {+ memset(&slruStats[i], 0, sizeof(slruStats[i]));+ slruStats[i].stat_reset_timestamp = ts;+ }+ }+}+
/* ----------
* pgstat_recv_autovac() -
*
@@ -6201,6 +6385,24 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
globalStats.buf_alloc += msg->m_buf_alloc;
}
+/* ----------+ * pgstat_recv_slru() -+ *+ * Process a SLRU message.+ * ----------+ */+static void+pgstat_recv_slru(PgStat_MsgSLRU *msg, int len)+{+ slruStats[msg->m_index].blocks_zeroed += msg->m_blocks_zeroed;+ slruStats[msg->m_index].blocks_hit += msg->m_blocks_hit;+ slruStats[msg->m_index].blocks_read += msg->m_blocks_read;+ slruStats[msg->m_index].blocks_written += msg->m_blocks_written;+ slruStats[msg->m_index].blocks_exists += msg->m_blocks_exists;+ slruStats[msg->m_index].flush += msg->m_flush;+ slruStats[msg->m_index].truncate += msg->m_truncate;+}+
/* ----------
* pgstat_recv_recoveryconflict() -
*
@@ -6455,3 +6657,101 @@ pgstat_clip_activity(const char *raw_activity)
return activity;
}
++/*+ * pgstat_slru_index+ *+ * Determine index of entry for a SLRU with a given name. If there's no exact+ * match, returns index of the last "other" entry used for SLRUs defined in+ * external proejcts.+ */+int+pgstat_slru_index(const char *name)+{+ int i;++ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ {+ if (strcmp(slru_names[i], name) == 0)+ return i;+ }++ /* return index of the last entry (which is the "other" one) */+ return (SLRU_NUM_ELEMENTS - 1);+}++/*+ * pgstat_slru_name+ *+ * Returns SLRU name for an index. The index may be above SLRU_NUM_ELEMENTS,+ * in which case this returns NULL. This allows writing code that does not+ * know the number of entries in advance.+ */+char *+pgstat_slru_name(int idx)+{+ Assert(idx >= 0);++ if (idx >= SLRU_NUM_ELEMENTS)+ return NULL;++ return slru_names[idx];+}++/*+ * slru_entry+ *+ * Returns pointer to entry with counters for given SLRU (based on the name+ * stored in SlruCtl as lwlock tranche name).+ */+static PgStat_MsgSLRU *+slru_entry(SlruCtl ctl)+{+ int idx = pgstat_slru_index(ctl->shared->lwlock_tranche_name);++ Assert((idx >= 0) && (idx < SLRU_NUM_ELEMENTS));++ return &SLRUStats[idx];+}++void+pgstat_slru_count_page_zeroed(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_zeroed += 1;+}++void+pgstat_slru_count_page_hit(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_hit += 1;+}++void+pgstat_slru_count_page_exists(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_exists += 1;+}++void+pgstat_slru_count_page_read(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_read += 1;+}++void+pgstat_slru_count_page_written(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_written += 1;+}++void+pgstat_slru_count_flush(SlruCtl ctl)+{+ slru_entry(ctl)->m_flush += 1;+}++void+pgstat_slru_count_truncate(SlruCtl ctl)+{+ slru_entry(ctl)->m_truncate += 1;+}diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.cindex cea01534a5..99b20de773 100644--- a/src/backend/utils/adt/pgstatfuncs.c+++ b/src/backend/utils/adt/pgstatfuncs.c@@ -1674,6 +1674,83 @@ pg_stat_get_buf_alloc(PG_FUNCTION_ARGS)
PG_RETURN_INT64(pgstat_fetch_global()->buf_alloc);
}
+/*+ * Returns statistics of SLRU caches.+ */+Datum+pg_stat_get_slru(PG_FUNCTION_ARGS)+{+#define PG_STAT_GET_SLRU_COLS 9+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;+ TupleDesc tupdesc;+ Tuplestorestate *tupstore;+ MemoryContext per_query_ctx;+ MemoryContext oldcontext;+ int i;+ PgStat_SLRUStats *stats;++ /* check to see if caller supports us returning a tuplestore */+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))+ ereport(ERROR,+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),+ errmsg("set-valued function called in context that cannot accept a set")));+ if (!(rsinfo->allowedModes & SFRM_Materialize))+ ereport(ERROR,+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),+ errmsg("materialize mode required, but it is not allowed in this context")));++ /* Build a tuple descriptor for our result type */+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)+ elog(ERROR, "return type must be a row type");++ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;+ oldcontext = MemoryContextSwitchTo(per_query_ctx);++ tupstore = tuplestore_begin_heap(true, false, work_mem);+ rsinfo->returnMode = SFRM_Materialize;+ rsinfo->setResult = tupstore;+ rsinfo->setDesc = tupdesc;++ MemoryContextSwitchTo(oldcontext);++ /* request SLRU stats from the stat collector */+ stats = pgstat_fetch_slru();++ for (i = 0; ; i++)+ {+ /* for each row */+ Datum values[PG_STAT_GET_SLRU_COLS];+ bool nulls[PG_STAT_GET_SLRU_COLS];+ PgStat_SLRUStats stat = stats[i];+ char *name;++ name = pgstat_slru_name(i);++ if (!name)+ break;++ MemSet(values, 0, sizeof(values));+ MemSet(nulls, 0, sizeof(nulls));++ values[0] = PointerGetDatum(cstring_to_text(name));+ values[1] = Int64GetDatum(stat.blocks_zeroed);+ values[2] = Int64GetDatum(stat.blocks_hit);+ values[3] = Int64GetDatum(stat.blocks_read);+ values[4] = Int64GetDatum(stat.blocks_written);+ values[5] = Int64GetDatum(stat.blocks_exists);+ values[6] = Int64GetDatum(stat.flush);+ values[7] = Int64GetDatum(stat.truncate);+ values[8] = Int64GetDatum(stat.stat_reset_timestamp);++ tuplestore_putvalues(tupstore, tupdesc, values, nulls);+ }++ /* clean up and return the tuplestore */+ tuplestore_donestoring(tupstore);++ return (Datum) 0;+}+
Datum
pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
{
@@ -1919,6 +1996,20 @@ pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
+/* Reset SLRU counters (a specific one or all of them). */+Datum+pg_stat_reset_slru(PG_FUNCTION_ARGS)+{+ char *target = NULL;++ if (!PG_ARGISNULL(0))+ target = text_to_cstring(PG_GETARG_TEXT_PP(0));++ pgstat_reset_slru_counter(target);++ PG_RETURN_VOID();+}+
Datum
pg_stat_get_archiver(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.datindex 87d25d4a4b..96a93d8570 100644--- a/src/include/catalog/pg_proc.dat+++ b/src/include/catalog/pg_proc.dat@@ -5431,6 +5431,16 @@
proname => 'pg_stat_get_buf_alloc', provolatile => 's', proparallel => 'r',
prorettype => 'int8', proargtypes => '', prosrc => 'pg_stat_get_buf_alloc' },
+{ oid => '8614',+ descr => 'statistics: information about SLRU caches',+ proname => 'pg_stat_get_slru', prorows => '100', proisstrict => 'f',+ proretset => 't', provolatile => 's', proparallel => 'r',+ prorettype => 'record', proargtypes => '',+ proallargtypes => '{text,int8,int8,int8,int8,int8,int8,int8,timestamptz}',+ proargmodes => '{o,o,o,o,o,o,o,o,o}',+ proargnames => '{name,blks_zeroed,blks_hit,blks_read,blks_written,blks_exists,flushes,truncates,stats_reset}',+ prosrc => 'pg_stat_get_slru' },+
{ oid => '2978', descr => 'statistics: number of function calls',
proname => 'pg_stat_get_function_calls', provolatile => 's',
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
@@ -5535,6 +5545,11 @@
proname => 'pg_stat_reset_single_function_counters', provolatile => 'v',
prorettype => 'void', proargtypes => 'oid',
prosrc => 'pg_stat_reset_single_function_counters' },
+{ oid => '4179',+ descr => 'statistics: reset collected statistics for a single SLRU',+ proname => 'pg_stat_reset_slru', provolatile => 'v', proisstrict => 'f',+ prorettype => 'void', proargtypes => 'text',+ prosrc => 'pg_stat_reset_slru' },
{ oid => '3163', descr => 'current trigger depth',
proname => 'pg_trigger_depth', provolatile => 's', proparallel => 'r',
diff --git a/src/include/pgstat.h b/src/include/pgstat.hindex a07012bf4b..0fac192ea1 100644--- a/src/include/pgstat.h+++ b/src/include/pgstat.h@@ -11,6 +11,7 @@
#ifndef PGSTAT_H
#define PGSTAT_H
+#include "access/slru.h"
#include "datatype/timestamp.h"
#include "libpq/pqcomm.h"
#include "miscadmin.h"
@@ -55,11 +56,13 @@ typedef enum StatMsgType
PGSTAT_MTYPE_RESETCOUNTER,
PGSTAT_MTYPE_RESETSHAREDCOUNTER,
PGSTAT_MTYPE_RESETSINGLECOUNTER,
+ PGSTAT_MTYPE_RESETSLRUCOUNTER,
PGSTAT_MTYPE_AUTOVAC_START,
PGSTAT_MTYPE_VACUUM,
PGSTAT_MTYPE_ANALYZE,
PGSTAT_MTYPE_ARCHIVER,
PGSTAT_MTYPE_BGWRITER,
+ PGSTAT_MTYPE_SLRU,
PGSTAT_MTYPE_FUNCSTAT,
PGSTAT_MTYPE_FUNCPURGE,
PGSTAT_MTYPE_RECOVERYCONFLICT,
@@ -343,6 +346,17 @@ typedef struct PgStat_MsgResetsinglecounter
Oid m_objectid;
} PgStat_MsgResetsinglecounter;
+/* ----------+ * PgStat_MsgResetslrucounter Sent by the backend to tell the collector+ * to reset a SLRU counter+ * ----------+ */+typedef struct PgStat_MsgResetslrucounter+{+ PgStat_MsgHdr m_hdr;+ int m_index;+} PgStat_MsgResetslrucounter;+
/* ----------
* PgStat_MsgAutovacStart Sent by the autovacuum daemon to signal
* that a database is going to be processed
@@ -423,6 +437,23 @@ typedef struct PgStat_MsgBgWriter
PgStat_Counter m_checkpoint_sync_time;
} PgStat_MsgBgWriter;
+/* ----------+ * PgStat_MsgSLRU Sent by the SLRU to update statistics.+ * ----------+ */+typedef struct PgStat_MsgSLRU+{+ PgStat_MsgHdr m_hdr;+ PgStat_Counter m_index;+ PgStat_Counter m_blocks_zeroed;+ PgStat_Counter m_blocks_hit;+ PgStat_Counter m_blocks_read;+ PgStat_Counter m_blocks_written;+ PgStat_Counter m_blocks_exists;+ PgStat_Counter m_flush;+ PgStat_Counter m_truncate;+} PgStat_MsgSLRU;+
/* ----------
* PgStat_MsgRecoveryConflict Sent by the backend upon recovery conflict
* ----------
@@ -560,11 +591,13 @@ typedef union PgStat_Msg
PgStat_MsgResetcounter msg_resetcounter;
PgStat_MsgResetsharedcounter msg_resetsharedcounter;
PgStat_MsgResetsinglecounter msg_resetsinglecounter;
+ PgStat_MsgResetslrucounter msg_resetslrucounter;
PgStat_MsgAutovacStart msg_autovacuum_start;
PgStat_MsgVacuum msg_vacuum;
PgStat_MsgAnalyze msg_analyze;
PgStat_MsgArchiver msg_archiver;
PgStat_MsgBgWriter msg_bgwriter;
+ PgStat_MsgSLRU msg_slru;
PgStat_MsgFuncstat msg_funcstat;
PgStat_MsgFuncpurge msg_funcpurge;
PgStat_MsgRecoveryConflict msg_recoveryconflict;
@@ -712,6 +745,21 @@ typedef struct PgStat_GlobalStats
TimestampTz stat_reset_timestamp;
} PgStat_GlobalStats;
+/*+ * SLRU statistics kept in the stats collector+ */+typedef struct PgStat_SLRUStats+{+ PgStat_Counter blocks_zeroed;+ PgStat_Counter blocks_hit;+ PgStat_Counter blocks_read;+ PgStat_Counter blocks_written;+ PgStat_Counter blocks_exists;+ PgStat_Counter flush;+ PgStat_Counter truncate;+ TimestampTz stat_reset_timestamp;+} PgStat_SLRUStats;+
/* ----------
* Backend states
@@ -1209,6 +1257,11 @@ extern char *pgstat_stat_filename;
*/
extern PgStat_MsgBgWriter BgWriterStats;
+/*+ * SLRU statistics counters are updated directly by slru.+ */+extern PgStat_MsgSLRU SlruStats[];+
/*
* Updated by pgstat_count_buffer_*_time macros
*/
@@ -1246,6 +1299,7 @@ extern void pgstat_clear_snapshot(void);
extern void pgstat_reset_counters(void);
extern void pgstat_reset_shared_counters(const char *);
extern void pgstat_reset_single_counter(Oid objectid, PgStat_Single_Reset_Type type);
+extern void pgstat_reset_slru_counter(const char *);
extern void pgstat_report_autovac(Oid dboid);
extern void pgstat_report_vacuum(Oid tableoid, bool shared,
@@ -1421,5 +1475,16 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
extern int pgstat_fetch_stat_numbackends(void);
extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_SLRUStats *pgstat_fetch_slru(void);++extern void pgstat_slru_count_page_zeroed(SlruCtl ctl);+extern void pgstat_slru_count_page_hit(SlruCtl ctl);+extern void pgstat_slru_count_page_read(SlruCtl ctl);+extern void pgstat_slru_count_page_written(SlruCtl ctl);+extern void pgstat_slru_count_page_exists(SlruCtl ctl);+extern void pgstat_slru_count_flush(SlruCtl ctl);+extern void pgstat_slru_count_truncate(SlruCtl ctl);+extern char *pgstat_slru_name(int idx);+extern int pgstat_slru_index(const char *name);
#endif /* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.outindex a2077bbad4..798364230e 100644--- a/src/test/regress/expected/rules.out+++ b/src/test/regress/expected/rules.out@@ -2006,6 +2006,16 @@ pg_stat_replication| SELECT s.pid,
FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time, spill_txns, spill_count, spill_bytes) ON ((s.pid = w.pid)))
LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
+pg_stat_slru| SELECT s.name,+ s.blks_zeroed,+ s.blks_hit,+ s.blks_read,+ s.blks_written,+ s.blks_exists,+ s.flushes,+ s.truncates,+ s.stats_reset+ FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset);
pg_stat_ssl| SELECT s.pid,
s.ssl,
s.sslversion AS version,
--
2.21.1
Attachments:
[text/plain] 0001-Collect-SLRU-statistics.patch (32.0K, ../20200328002630.2nz5y3dvymqnvxtv@development/2-0001-Collect-SLRU-statistics.patch)
download | inline diff:
From 1a065c21a9a791909cd1ca752db5aaf1f814fe37 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tv@fuzzy.cz>
Date: Thu, 26 Mar 2020 20:52:26 +0100
Subject: [PATCH] Collect SLRU statistics
Adds a new system view pg_stats_slru with stats about SLRU caches, and
a function pg_stat_reset_slru() to reset either all counters or just
counters for a single SLRU.
There is no SLRU registry this patch could use, so it simply uses the
SLRU name (which is also used for LWLock tranche name) as an identifier,
and a predefined list of SLRU names, and an extra "others" entry for
SLRUs without a dedicated entry. Presumably, the number of extensions
defining their own SLRU is very small.
Author: Tomas Vondra
Reviewed-by: Alvaro Herrera
Discussion: https://www.postgresql.org/message-id/flat/20200119143707.gyinppnigokesjok@development
---
doc/src/sgml/monitoring.sgml | 97 +++++++++
src/backend/access/transam/slru.c | 23 ++
src/backend/catalog/system_views.sql | 14 ++
src/backend/postmaster/pgstat.c | 300 +++++++++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 91 ++++++++
src/include/catalog/pg_proc.dat | 15 ++
src/include/pgstat.h | 65 ++++++
src/test/regress/expected/rules.out | 10 +
8 files changed, 615 insertions(+)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgmlindex 270178d57e..7ba0dbee6a 100644--- a/doc/src/sgml/monitoring.sgml+++ b/doc/src/sgml/monitoring.sgml@@ -575,6 +575,13 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
yet included in <structname>pg_stat_user_functions</structname>).</entry>
</row>
+ <row>+ <entry><structname>pg_stat_slru</structname><indexterm><primary>pg_stat_slru</primary></indexterm></entry>+ <entry>One row per SLRU, showing statistics of operations. See+ <xref linkend="pg-stat-slru-view"/> for details.+ </entry>+ </row>+
</tbody>
</tgroup>
</table>
@@ -3254,6 +3261,76 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
</tgroup>
</table>
+ <para>+ The <structname>pg_stat_slru</structname> view will contain+ one row for each tracked SLRU cache, showing statistics about access+ to cached pages.+ </para>++ <table id="pg-stat-slru-view" xreflabel="pg_stat_slru">+ <title><structname>pg_stat_slru</structname> View</title>+ <tgroup cols="3">+ <thead>+ <row>+ <entry>Column</entry>+ <entry>Type</entry>+ <entry>Description</entry>+ </row>+ </thead>++ <tbody>+ <row>+ <entry><structfield>name</structfield></entry>+ <entry><type>name</type></entry>+ <entry>name of the SLRU</entry>+ </row>+ <row>+ <entry><structfield>blks_zeroed</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of blocks zeroed during initializations</entry>+ </row>+ <row>+ <entry><structfield>blks_hit</structfield></entry>+ <entry><type>biging</type></entry>+ <entry>Number of times disk blocks were found already in the SLRU,+ so that a read was not necessary (this only includes hits in the+ SLRU, not the operating system's file system cache)+ </entry>+ </row>+ <row>+ <entry><structfield>blks_read</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of disk blocks read for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>blks_written</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of disk blocks written for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>blks_exists</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of blocks checked for existence for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>flushes</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of flushes of dirty data for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>truncates</structfield></entry>+ <entry><type>bigint</type></entry>+ <entry>Number of truncates for this SLRU</entry>+ </row>+ <row>+ <entry><structfield>stats_reset</structfield></entry>+ <entry><type>timestamp with time zone</type></entry>+ <entry>Time at which these statistics were last reset</entry>+ </row>+ </tbody>+ </tgroup>+ </table>+
<para>
The <structname>pg_stat_user_functions</structname> view will contain
one row for each tracked function, showing statistics about executions of
@@ -3378,6 +3455,26 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
function can be granted to others)
</entry>
</row>
++ <row>+ <entry><literal><function>pg_stat_reset_slru</function>(text)</literal><indexterm><primary>pg_stat_reset_slru</primary></indexterm></entry>+ <entry><type>void</type></entry>+ <entry>+ Reset statistics either for a single SLRU or all SLRUs in the cluster+ to zero (requires superuser privileges by default, but EXECUTE for this+ function can be granted to others).+ Calling <literal>pg_stat_reset_slru(NULL)</literal> will zero all the+ counters shown in the <structname>pg_stat_slru</structname> view for+ all SLRU caches.+ Calling <literal>pg_stat_reset_slru(name)</literal> with names from a+ predefined list (<literal>async</literal>, <literal>clog</literal>,+ <literal>commit_timestamp</literal>, <literal>multixact_offset</literal>,+ <literal>multixact_member</literal>, <literal>oldserxid</literal>,+ <literal>pg_xact</literal>, <literal>subtrans</literal> and+ <literal>other</literal>) resets counters for only that entry.+ Names not included in this list are treated as <literal>other</literal>.+ </entry>+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.cindex d5b7a08f73..f7160dd574 100644--- a/src/backend/access/transam/slru.c+++ b/src/backend/access/transam/slru.c@@ -286,6 +286,9 @@ SimpleLruZeroPage(SlruCtl ctl, int pageno)
/* Assume this page is now the latest active page */
shared->latest_page_number = pageno;
+ /* update the stats counter of zeroed pages */+ pgstat_slru_count_page_zeroed(ctl);+
return slotno;
}
@@ -403,6 +406,10 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
}
/* Otherwise, it's ready to use */
SlruRecentlyUsed(shared, slotno);
++ /* update the stats counter of pages found in the SLRU */+ pgstat_slru_count_page_hit(ctl);+
return slotno;
}
@@ -444,6 +451,10 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
SlruReportIOError(ctl, pageno, xid);
SlruRecentlyUsed(shared, slotno);
++ /* update the stats counter of pages not found in SLRU */+ pgstat_slru_count_page_read(ctl);+
return slotno;
}
}
@@ -596,6 +607,9 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
bool result;
off_t endpos;
+ /* update the stats counter of checked pages */+ pgstat_slru_count_page_exists(ctl);+
SlruFileName(ctl, path, segno);
fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
@@ -730,6 +744,9 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruFlush fdata)
char path[MAXPGPATH];
int fd = -1;
+ /* update the stats counter of written pages */+ pgstat_slru_count_page_written(ctl);+
/*
* Honor the write-WAL-before-data rule, if appropriate, so that we do not
* write out data before associated WAL records. This is the same action
@@ -1125,6 +1142,9 @@ SimpleLruFlush(SlruCtl ctl, bool allow_redirtied)
int i;
bool ok;
+ /* update the stats counter of flushes */+ pgstat_slru_count_flush(ctl);+
/*
* Find and write dirty pages
*/
@@ -1186,6 +1206,9 @@ SimpleLruTruncate(SlruCtl ctl, int cutoffPage)
SlruShared shared = ctl->shared;
int slotno;
+ /* update the stats counter of truncates */+ pgstat_slru_count_truncate(ctl);+
/*
* The cutoff point is the start of the segment containing cutoffPage.
*/
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sqlindex 5a6dc61630..09e226f34d 100644--- a/src/backend/catalog/system_views.sql+++ b/src/backend/catalog/system_views.sql@@ -792,6 +792,19 @@ CREATE VIEW pg_stat_replication AS
JOIN pg_stat_get_wal_senders() AS W ON (S.pid = W.pid)
LEFT JOIN pg_authid AS U ON (S.usesysid = U.oid);
+CREATE VIEW pg_stat_slru AS+ SELECT+ s.name,+ s.blks_zeroed,+ s.blks_hit,+ s.blks_read,+ s.blks_written,+ s.blks_exists,+ s.flushes,+ s.truncates,+ s.stats_reset+ FROM pg_stat_get_slru() s;+
CREATE VIEW pg_stat_wal_receiver AS
SELECT
s.pid,
@@ -1409,6 +1422,7 @@ REVOKE EXECUTE ON FUNCTION pg_promote(boolean, integer) FROM public;
REVOKE EXECUTE ON FUNCTION pg_stat_reset() FROM public;
REVOKE EXECUTE ON FUNCTION pg_stat_reset_shared(text) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_stat_reset_slru(text) FROM public;
REVOKE EXECUTE ON FUNCTION pg_stat_reset_single_table_counters(oid) FROM public;
REVOKE EXECUTE ON FUNCTION pg_stat_reset_single_function_counters(oid) FROM public;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.cindex 4763c24be9..ea0fdade58 100644--- a/src/backend/postmaster/pgstat.c+++ b/src/backend/postmaster/pgstat.c@@ -141,6 +141,24 @@ char *pgstat_stat_tmpname = NULL;
*/
PgStat_MsgBgWriter BgWriterStats;
+/*+ * SLRU statistics counters (unused in other processes) stored directly in+ * stats structure so it can be sent without needing to copy things around.+ * We assume this inits to zeroes.+ *+ * There's a separte entry for each SLRU we have. The "other" entry is used+ * for all SLRUs without an explicit entry (e.g. SLRUs in extensions).+ */+static char *slru_names[] = {"async", "clog", "commit_timestamp",+ "multixact_offset", "multixact_member",+ "oldserxid", "pg_xact", "subtrans",+ "other" /* has to be last */};++#define SLRU_NUM_ELEMENTS (sizeof(slru_names) / sizeof(char *))++/* entries in the same order as slru_names */+PgStat_MsgSLRU SLRUStats[SLRU_NUM_ELEMENTS];+
/* ----------
* Local data
* ----------
@@ -255,6 +273,7 @@ static int localNumBackends = 0;
*/
static PgStat_ArchiverStats archiverStats;
static PgStat_GlobalStats globalStats;
+static PgStat_SLRUStats slruStats[SLRU_NUM_ELEMENTS];
/*
* List of OIDs of databases we need to write out. If an entry is InvalidOid,
@@ -297,6 +316,7 @@ static bool pgstat_db_requested(Oid databaseid);
static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg);
static void pgstat_send_funcstats(void);
+static void pgstat_send_slru(void);
static HTAB *pgstat_collect_oids(Oid catalogid, AttrNumber anum_oid);
static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared);
@@ -319,11 +339,13 @@ static void pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len);
static void pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len);
static void pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len);
static void pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len);
+static void pgstat_recv_resetslrucounter(PgStat_MsgResetslrucounter *msg, int len);
static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len);
static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len);
static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len);
static void pgstat_recv_archiver(PgStat_MsgArchiver *msg, int len);
static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len);
+static void pgstat_recv_slru(PgStat_MsgSLRU *msg, int len);
static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len);
static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
@@ -907,6 +929,9 @@ pgstat_report_stat(bool force)
/* Now, send function statistics */
pgstat_send_funcstats();
++ /* Finally send SLRU statistics */+ pgstat_send_slru();
}
/*
@@ -1372,6 +1397,30 @@ pgstat_reset_single_counter(Oid objoid, PgStat_Single_Reset_Type type)
pgstat_send(&msg, sizeof(msg));
}
+/* ----------+ * pgstat_reset_slru_counter() -+ *+ * Tell the statistics collector to reset a single SLRU counter, or all+ * SLRU counters (when name is null).+ *+ * Permission checking for this function is managed through the normal+ * GRANT system.+ * ----------+ */+void+pgstat_reset_slru_counter(const char *name)+{+ PgStat_MsgResetslrucounter msg;++ if (pgStatSock == PGINVALID_SOCKET)+ return;++ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSLRUCOUNTER);+ msg.m_index = (name) ? pgstat_slru_index(name) : -1;++ pgstat_send(&msg, sizeof(msg));+}+
/* ----------
* pgstat_report_autovac() -
*
@@ -2622,6 +2671,23 @@ pgstat_fetch_global(void)
}
+/*+ * ---------+ * pgstat_fetch_slru() -+ *+ * Support function for the SQL-callable pgstat* functions. Returns+ * a pointer to the slru statistics struct.+ * ---------+ */+PgStat_SLRUStats *+pgstat_fetch_slru(void)+{+ backend_read_statsfile();++ return slruStats;+}++
/* ------------------------------------------------------------
* Functions for management of the shared-memory PgBackendStatus array
* ------------------------------------------------------------
@@ -4325,6 +4391,46 @@ pgstat_send_bgwriter(void)
MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
}
+/* ----------+ * pgstat_send_slru() -+ *+ * Send SLRU statistics to the collector+ * ----------+ */+static void+pgstat_send_slru(void)+{+ int i;++ /* We assume this initializes to zeroes */+ static const PgStat_MsgSLRU all_zeroes;++ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ {+ /*+ * This function can be called even if nothing at all has happened. In+ * this case, avoid sending a completely empty message to the stats+ * collector.+ */+ if (memcmp(&SLRUStats[i], &all_zeroes, sizeof(PgStat_MsgSLRU)) == 0)+ continue;++ /* set the SLRU type before each send */+ SLRUStats[i].m_index = i;++ /*+ * Prepare and send the message+ */+ pgstat_setheader(&SLRUStats[i].m_hdr, PGSTAT_MTYPE_SLRU);+ pgstat_send(&SLRUStats[i], sizeof(PgStat_MsgSLRU));++ /*+ * Clear out the statistics buffer, so it can be re-used.+ */+ MemSet(&SLRUStats[i], 0, sizeof(PgStat_MsgSLRU));+ }+}+
/* ----------
* PgstatCollectorMain() -
@@ -4493,6 +4599,11 @@ PgstatCollectorMain(int argc, char *argv[])
len);
break;
+ case PGSTAT_MTYPE_RESETSLRUCOUNTER:+ pgstat_recv_resetslrucounter(&msg.msg_resetslrucounter,+ len);+ break;+
case PGSTAT_MTYPE_AUTOVAC_START:
pgstat_recv_autovac(&msg.msg_autovacuum_start, len);
break;
@@ -4513,6 +4624,10 @@ PgstatCollectorMain(int argc, char *argv[])
pgstat_recv_bgwriter(&msg.msg_bgwriter, len);
break;
+ case PGSTAT_MTYPE_SLRU:+ pgstat_recv_slru(&msg.msg_slru, len);+ break;+
case PGSTAT_MTYPE_FUNCSTAT:
pgstat_recv_funcstat(&msg.msg_funcstat, len);
break;
@@ -4781,6 +4896,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
rc = fwrite(&archiverStats, sizeof(archiverStats), 1, fpout);
(void) rc; /* we'll check for error with ferror */
+ /*+ * Write SLRU stats struct+ */+ rc = fwrite(slruStats, sizeof(slruStats), 1, fpout);+ (void) rc; /* we'll check for error with ferror */+
/*
* Walk through the database table.
*/
@@ -5016,6 +5137,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
int32 format_id;
bool found;
const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
+ int i;
/*
* The tables will live in pgStatLocalContext.
@@ -5038,6 +5160,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
*/
memset(&globalStats, 0, sizeof(globalStats));
memset(&archiverStats, 0, sizeof(archiverStats));
+ memset(&slruStats, 0, sizeof(slruStats));
/*
* Set the current timestamp (will be kept only in case we can't load an
@@ -5046,6 +5169,13 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
globalStats.stat_reset_timestamp = GetCurrentTimestamp();
archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+ /*+ * Set the same reset timestamp for all SLRU items (one+ * day we might allow resetting individual SLRUs).+ */+ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ slruStats[i].stat_reset_timestamp = globalStats.stat_reset_timestamp;+
/*
* Try to open the stats file. If it doesn't exist, the backends simply
* return zero for anything and the collector simply starts from scratch
@@ -5108,6 +5238,17 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
goto done;
}
+ /*+ * Read SLRU stats struct+ */+ if (fread(slruStats, 1, sizeof(slruStats), fpin) != sizeof(slruStats))+ {+ ereport(pgStatRunningInCollector ? LOG : WARNING,+ (errmsg("corrupted statistics file \"%s\"", statfile)));+ memset(&slruStats, 0, sizeof(slruStats));+ goto done;+ }+
/*
* We found an existing collector stats file. Read it and put all the
* hashtable entries into place.
@@ -5406,9 +5547,11 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
PgStat_StatDBEntry dbentry;
PgStat_GlobalStats myGlobalStats;
PgStat_ArchiverStats myArchiverStats;
+ PgStat_SLRUStats mySLRUStats;
FILE *fpin;
int32 format_id;
const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
+ int i;
/*
* Try to open the stats file. As above, anything but ENOENT is worthy of
@@ -5460,6 +5603,21 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
return false;
}
+ /*+ * Read SLRU stats struct+ */+ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ {+ if (fread(&mySLRUStats, 1, sizeof(PgStat_SLRUStats),+ fpin) != sizeof(PgStat_SLRUStats))+ {+ ereport(pgStatRunningInCollector ? LOG : WARNING,+ (errmsg("corrupted statistics file \"%s\"", statfile)));+ FreeFile(fpin);+ return false;+ }+ }+
/* By default, we're going to return the timestamp of the global file. */
*ts = myGlobalStats.stats_timestamp;
@@ -6057,6 +6215,32 @@ pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len)
HASH_REMOVE, NULL);
}
+/* ----------+ * pgstat_recv_resetslrucounter() -+ *+ * Reset some SLRU statistics of the cluster.+ * ----------+ */+static void+pgstat_recv_resetslrucounter(PgStat_MsgResetslrucounter *msg, int len)+{+ int i;+ TimestampTz ts = GetCurrentTimestamp();++ memset(&slruStats, 0, sizeof(slruStats));++ elog(LOG, "msg->m_index = %d", msg->m_index);++ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ {+ if ((msg->m_index == -1) || (msg->m_index == i))+ {+ memset(&slruStats[i], 0, sizeof(slruStats[i]));+ slruStats[i].stat_reset_timestamp = ts;+ }+ }+}+
/* ----------
* pgstat_recv_autovac() -
*
@@ -6201,6 +6385,24 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
globalStats.buf_alloc += msg->m_buf_alloc;
}
+/* ----------+ * pgstat_recv_slru() -+ *+ * Process a SLRU message.+ * ----------+ */+static void+pgstat_recv_slru(PgStat_MsgSLRU *msg, int len)+{+ slruStats[msg->m_index].blocks_zeroed += msg->m_blocks_zeroed;+ slruStats[msg->m_index].blocks_hit += msg->m_blocks_hit;+ slruStats[msg->m_index].blocks_read += msg->m_blocks_read;+ slruStats[msg->m_index].blocks_written += msg->m_blocks_written;+ slruStats[msg->m_index].blocks_exists += msg->m_blocks_exists;+ slruStats[msg->m_index].flush += msg->m_flush;+ slruStats[msg->m_index].truncate += msg->m_truncate;+}+
/* ----------
* pgstat_recv_recoveryconflict() -
*
@@ -6455,3 +6657,101 @@ pgstat_clip_activity(const char *raw_activity)
return activity;
}
++/*+ * pgstat_slru_index+ *+ * Determine index of entry for a SLRU with a given name. If there's no exact+ * match, returns index of the last "other" entry used for SLRUs defined in+ * external proejcts.+ */+int+pgstat_slru_index(const char *name)+{+ int i;++ for (i = 0; i < SLRU_NUM_ELEMENTS; i++)+ {+ if (strcmp(slru_names[i], name) == 0)+ return i;+ }++ /* return index of the last entry (which is the "other" one) */+ return (SLRU_NUM_ELEMENTS - 1);+}++/*+ * pgstat_slru_name+ *+ * Returns SLRU name for an index. The index may be above SLRU_NUM_ELEMENTS,+ * in which case this returns NULL. This allows writing code that does not+ * know the number of entries in advance.+ */+char *+pgstat_slru_name(int idx)+{+ Assert(idx >= 0);++ if (idx >= SLRU_NUM_ELEMENTS)+ return NULL;++ return slru_names[idx];+}++/*+ * slru_entry+ *+ * Returns pointer to entry with counters for given SLRU (based on the name+ * stored in SlruCtl as lwlock tranche name).+ */+static PgStat_MsgSLRU *+slru_entry(SlruCtl ctl)+{+ int idx = pgstat_slru_index(ctl->shared->lwlock_tranche_name);++ Assert((idx >= 0) && (idx < SLRU_NUM_ELEMENTS));++ return &SLRUStats[idx];+}++void+pgstat_slru_count_page_zeroed(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_zeroed += 1;+}++void+pgstat_slru_count_page_hit(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_hit += 1;+}++void+pgstat_slru_count_page_exists(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_exists += 1;+}++void+pgstat_slru_count_page_read(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_read += 1;+}++void+pgstat_slru_count_page_written(SlruCtl ctl)+{+ slru_entry(ctl)->m_blocks_written += 1;+}++void+pgstat_slru_count_flush(SlruCtl ctl)+{+ slru_entry(ctl)->m_flush += 1;+}++void+pgstat_slru_count_truncate(SlruCtl ctl)+{+ slru_entry(ctl)->m_truncate += 1;+}diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.cindex cea01534a5..99b20de773 100644--- a/src/backend/utils/adt/pgstatfuncs.c+++ b/src/backend/utils/adt/pgstatfuncs.c@@ -1674,6 +1674,83 @@ pg_stat_get_buf_alloc(PG_FUNCTION_ARGS)
PG_RETURN_INT64(pgstat_fetch_global()->buf_alloc);
}
+/*+ * Returns statistics of SLRU caches.+ */+Datum+pg_stat_get_slru(PG_FUNCTION_ARGS)+{+#define PG_STAT_GET_SLRU_COLS 9+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;+ TupleDesc tupdesc;+ Tuplestorestate *tupstore;+ MemoryContext per_query_ctx;+ MemoryContext oldcontext;+ int i;+ PgStat_SLRUStats *stats;++ /* check to see if caller supports us returning a tuplestore */+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))+ ereport(ERROR,+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),+ errmsg("set-valued function called in context that cannot accept a set")));+ if (!(rsinfo->allowedModes & SFRM_Materialize))+ ereport(ERROR,+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),+ errmsg("materialize mode required, but it is not allowed in this context")));++ /* Build a tuple descriptor for our result type */+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)+ elog(ERROR, "return type must be a row type");++ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;+ oldcontext = MemoryContextSwitchTo(per_query_ctx);++ tupstore = tuplestore_begin_heap(true, false, work_mem);+ rsinfo->returnMode = SFRM_Materialize;+ rsinfo->setResult = tupstore;+ rsinfo->setDesc = tupdesc;++ MemoryContextSwitchTo(oldcontext);++ /* request SLRU stats from the stat collector */+ stats = pgstat_fetch_slru();++ for (i = 0; ; i++)+ {+ /* for each row */+ Datum values[PG_STAT_GET_SLRU_COLS];+ bool nulls[PG_STAT_GET_SLRU_COLS];+ PgStat_SLRUStats stat = stats[i];+ char *name;++ name = pgstat_slru_name(i);++ if (!name)+ break;++ MemSet(values, 0, sizeof(values));+ MemSet(nulls, 0, sizeof(nulls));++ values[0] = PointerGetDatum(cstring_to_text(name));+ values[1] = Int64GetDatum(stat.blocks_zeroed);+ values[2] = Int64GetDatum(stat.blocks_hit);+ values[3] = Int64GetDatum(stat.blocks_read);+ values[4] = Int64GetDatum(stat.blocks_written);+ values[5] = Int64GetDatum(stat.blocks_exists);+ values[6] = Int64GetDatum(stat.flush);+ values[7] = Int64GetDatum(stat.truncate);+ values[8] = Int64GetDatum(stat.stat_reset_timestamp);++ tuplestore_putvalues(tupstore, tupdesc, values, nulls);+ }++ /* clean up and return the tuplestore */+ tuplestore_donestoring(tupstore);++ return (Datum) 0;+}+
Datum
pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
{
@@ -1919,6 +1996,20 @@ pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
+/* Reset SLRU counters (a specific one or all of them). */+Datum+pg_stat_reset_slru(PG_FUNCTION_ARGS)+{+ char *target = NULL;++ if (!PG_ARGISNULL(0))+ target = text_to_cstring(PG_GETARG_TEXT_PP(0));++ pgstat_reset_slru_counter(target);++ PG_RETURN_VOID();+}+
Datum
pg_stat_get_archiver(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.datindex 87d25d4a4b..96a93d8570 100644--- a/src/include/catalog/pg_proc.dat+++ b/src/include/catalog/pg_proc.dat@@ -5431,6 +5431,16 @@
proname => 'pg_stat_get_buf_alloc', provolatile => 's', proparallel => 'r',
prorettype => 'int8', proargtypes => '', prosrc => 'pg_stat_get_buf_alloc' },
+{ oid => '8614',+ descr => 'statistics: information about SLRU caches',+ proname => 'pg_stat_get_slru', prorows => '100', proisstrict => 'f',+ proretset => 't', provolatile => 's', proparallel => 'r',+ prorettype => 'record', proargtypes => '',+ proallargtypes => '{text,int8,int8,int8,int8,int8,int8,int8,timestamptz}',+ proargmodes => '{o,o,o,o,o,o,o,o,o}',+ proargnames => '{name,blks_zeroed,blks_hit,blks_read,blks_written,blks_exists,flushes,truncates,stats_reset}',+ prosrc => 'pg_stat_get_slru' },+
{ oid => '2978', descr => 'statistics: number of function calls',
proname => 'pg_stat_get_function_calls', provolatile => 's',
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
@@ -5535,6 +5545,11 @@
proname => 'pg_stat_reset_single_function_counters', provolatile => 'v',
prorettype => 'void', proargtypes => 'oid',
prosrc => 'pg_stat_reset_single_function_counters' },
+{ oid => '4179',+ descr => 'statistics: reset collected statistics for a single SLRU',+ proname => 'pg_stat_reset_slru', provolatile => 'v', proisstrict => 'f',+ prorettype => 'void', proargtypes => 'text',+ prosrc => 'pg_stat_reset_slru' },
{ oid => '3163', descr => 'current trigger depth',
proname => 'pg_trigger_depth', provolatile => 's', proparallel => 'r',
diff --git a/src/include/pgstat.h b/src/include/pgstat.hindex a07012bf4b..0fac192ea1 100644--- a/src/include/pgstat.h+++ b/src/include/pgstat.h@@ -11,6 +11,7 @@
#ifndef PGSTAT_H
#define PGSTAT_H
+#include "access/slru.h"
#include "datatype/timestamp.h"
#include "libpq/pqcomm.h"
#include "miscadmin.h"
@@ -55,11 +56,13 @@ typedef enum StatMsgType
PGSTAT_MTYPE_RESETCOUNTER,
PGSTAT_MTYPE_RESETSHAREDCOUNTER,
PGSTAT_MTYPE_RESETSINGLECOUNTER,
+ PGSTAT_MTYPE_RESETSLRUCOUNTER,
PGSTAT_MTYPE_AUTOVAC_START,
PGSTAT_MTYPE_VACUUM,
PGSTAT_MTYPE_ANALYZE,
PGSTAT_MTYPE_ARCHIVER,
PGSTAT_MTYPE_BGWRITER,
+ PGSTAT_MTYPE_SLRU,
PGSTAT_MTYPE_FUNCSTAT,
PGSTAT_MTYPE_FUNCPURGE,
PGSTAT_MTYPE_RECOVERYCONFLICT,
@@ -343,6 +346,17 @@ typedef struct PgStat_MsgResetsinglecounter
Oid m_objectid;
} PgStat_MsgResetsinglecounter;
+/* ----------+ * PgStat_MsgResetslrucounter Sent by the backend to tell the collector+ * to reset a SLRU counter+ * ----------+ */+typedef struct PgStat_MsgResetslrucounter+{+ PgStat_MsgHdr m_hdr;+ int m_index;+} PgStat_MsgResetslrucounter;+
/* ----------
* PgStat_MsgAutovacStart Sent by the autovacuum daemon to signal
* that a database is going to be processed
@@ -423,6 +437,23 @@ typedef struct PgStat_MsgBgWriter
PgStat_Counter m_checkpoint_sync_time;
} PgStat_MsgBgWriter;
+/* ----------+ * PgStat_MsgSLRU Sent by the SLRU to update statistics.+ * ----------+ */+typedef struct PgStat_MsgSLRU+{+ PgStat_MsgHdr m_hdr;+ PgStat_Counter m_index;+ PgStat_Counter m_blocks_zeroed;+ PgStat_Counter m_blocks_hit;+ PgStat_Counter m_blocks_read;+ PgStat_Counter m_blocks_written;+ PgStat_Counter m_blocks_exists;+ PgStat_Counter m_flush;+ PgStat_Counter m_truncate;+} PgStat_MsgSLRU;+
/* ----------
* PgStat_MsgRecoveryConflict Sent by the backend upon recovery conflict
* ----------
@@ -560,11 +591,13 @@ typedef union PgStat_Msg
PgStat_MsgResetcounter msg_resetcounter;
PgStat_MsgResetsharedcounter msg_resetsharedcounter;
PgStat_MsgResetsinglecounter msg_resetsinglecounter;
+ PgStat_MsgResetslrucounter msg_resetslrucounter;
PgStat_MsgAutovacStart msg_autovacuum_start;
PgStat_MsgVacuum msg_vacuum;
PgStat_MsgAnalyze msg_analyze;
PgStat_MsgArchiver msg_archiver;
PgStat_MsgBgWriter msg_bgwriter;
+ PgStat_MsgSLRU msg_slru;
PgStat_MsgFuncstat msg_funcstat;
PgStat_MsgFuncpurge msg_funcpurge;
PgStat_MsgRecoveryConflict msg_recoveryconflict;
@@ -712,6 +745,21 @@ typedef struct PgStat_GlobalStats
TimestampTz stat_reset_timestamp;
} PgStat_GlobalStats;
+/*+ * SLRU statistics kept in the stats collector+ */+typedef struct PgStat_SLRUStats+{+ PgStat_Counter blocks_zeroed;+ PgStat_Counter blocks_hit;+ PgStat_Counter blocks_read;+ PgStat_Counter blocks_written;+ PgStat_Counter blocks_exists;+ PgStat_Counter flush;+ PgStat_Counter truncate;+ TimestampTz stat_reset_timestamp;+} PgStat_SLRUStats;+
/* ----------
* Backend states
@@ -1209,6 +1257,11 @@ extern char *pgstat_stat_filename;
*/
extern PgStat_MsgBgWriter BgWriterStats;
+/*+ * SLRU statistics counters are updated directly by slru.+ */+extern PgStat_MsgSLRU SlruStats[];+
/*
* Updated by pgstat_count_buffer_*_time macros
*/
@@ -1246,6 +1299,7 @@ extern void pgstat_clear_snapshot(void);
extern void pgstat_reset_counters(void);
extern void pgstat_reset_shared_counters(const char *);
extern void pgstat_reset_single_counter(Oid objectid, PgStat_Single_Reset_Type type);
+extern void pgstat_reset_slru_counter(const char *);
extern void pgstat_report_autovac(Oid dboid);
extern void pgstat_report_vacuum(Oid tableoid, bool shared,
@@ -1421,5 +1475,16 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
extern int pgstat_fetch_stat_numbackends(void);
extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_SLRUStats *pgstat_fetch_slru(void);++extern void pgstat_slru_count_page_zeroed(SlruCtl ctl);+extern void pgstat_slru_count_page_hit(SlruCtl ctl);+extern void pgstat_slru_count_page_read(SlruCtl ctl);+extern void pgstat_slru_count_page_written(SlruCtl ctl);+extern void pgstat_slru_count_page_exists(SlruCtl ctl);+extern void pgstat_slru_count_flush(SlruCtl ctl);+extern void pgstat_slru_count_truncate(SlruCtl ctl);+extern char *pgstat_slru_name(int idx);+extern int pgstat_slru_index(const char *name);
#endif /* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.outindex a2077bbad4..798364230e 100644--- a/src/test/regress/expected/rules.out+++ b/src/test/regress/expected/rules.out@@ -2006,6 +2006,16 @@ pg_stat_replication| SELECT s.pid,
FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time, spill_txns, spill_count, spill_bytes) ON ((s.pid = w.pid)))
LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
+pg_stat_slru| SELECT s.name,+ s.blks_zeroed,+ s.blks_hit,+ s.blks_read,+ s.blks_written,+ s.blks_exists,+ s.flushes,+ s.truncates,+ s.stats_reset+ FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset);
pg_stat_ssl| SELECT s.pid,
s.ssl,
s.sslversion AS version,
--
2.21.1
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: tomas.vondra@2ndquadrant.com, alvherre@2ndquadrant.com, masahiko.sawada@2ndquadrant.com, tsunakawa.takay@fujitsu.com
Subject: Re: SLRU statistics
In-Reply-To: <20200328002630.2nz5y3dvymqnvxtv@development>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox