Collect statistics about SLRU caches

There's a number of SLRU caches used to access important data like clog,
commit timestamps, multixact, asynchronous notifications, etc. Until now
we had no easy way to monitor these shared caches, compute hit ratios,
number of reads/writes etc.

This commit extends the statistics collector to track this information
for a predefined list of SLRUs, and also introduces a new system view
pg_stat_slru displaying the data.

The list of built-in SLRUs is fixed, but additional SLRUs may be defined
in extensions. Unfortunately, there's no suitable registry of SLRUs, so
this patch simply defines a fixed list of SLRUs with entries for the
built-in ones and one entry for all additional SLRUs. Extensions adding
their own SLRU are fairly rare, so this seems acceptable.

This patch only allows monitoring of SLRUs, not tuning. The SLRU sizes
are still fixed (hard-coded in the code) and it's not entirely clear
which of the SLRUs might need a GUC to tune size. In a way, allowing us
to determine that is one of the goals of this patch.

Bump catversion as the patch introduces new functions and system view.

Author: Tomas Vondra
Reviewed-by: Alvaro Herrera
Discussion: https://www.postgresql.org/message-id/flat/20200119143707.gyinppnigokesjok@development
This commit is contained in:
Tomas Vondra 2020-04-02 02:11:38 +02:00
parent 17ca067995
commit 28cac71bd3
9 changed files with 613 additions and 1 deletions

View File

@ -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>
@ -3259,6 +3266,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
@ -3383,6 +3460,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>

View File

@ -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_count_slru_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_count_slru_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_count_slru_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_count_slru_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_count_slru_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_count_slru_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_count_slru_truncate(ctl);
/*
* The cutoff point is the start of the segment containing cutoffPage.
*/

View File

@ -793,6 +793,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,
@ -1410,6 +1423,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;

View File

@ -141,6 +141,26 @@ 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 is no central registry of SLRUs,
* so we use this fixed list instead.
*
* 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 */};
/* number of elemenents of slru_name array */
#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 +275,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 +318,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 +341,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 +931,9 @@ pgstat_report_stat(bool force)
/* Now, send function statistics */
pgstat_send_funcstats();
/* Finally send SLRU statistics */
pgstat_send_slru();
}
/*
@ -1372,6 +1399,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 +2673,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 +4393,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 +4601,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 +4626,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;
@ -4782,6 +4899,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.
*/
@ -5017,6 +5140,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.
@ -5039,6 +5163,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
@ -5047,6 +5172,12 @@ 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 too.
*/
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
@ -5109,6 +5240,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.
@ -5407,6 +5549,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
PgStat_StatDBEntry dbentry;
PgStat_GlobalStats myGlobalStats;
PgStat_ArchiverStats myArchiverStats;
PgStat_SLRUStats mySLRUStats[SLRU_NUM_ELEMENTS];
FILE *fpin;
int32 format_id;
const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@ -5461,6 +5604,17 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
return false;
}
/*
* Read SLRU stats struct
*/
if (fread(mySLRUStats, 1, sizeof(mySLRUStats), fpin) != sizeof(mySLRUStats))
{
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;
@ -6061,6 +6215,33 @@ 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++)
{
/* reset entry with the given index, or all entries (index is -1) */
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() -
*
@ -6217,6 +6398,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() -
*
@ -6471,3 +6670,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_count_slru_page_zeroed(SlruCtl ctl)
{
slru_entry(ctl)->m_blocks_zeroed += 1;
}
void
pgstat_count_slru_page_hit(SlruCtl ctl)
{
slru_entry(ctl)->m_blocks_hit += 1;
}
void
pgstat_count_slru_page_exists(SlruCtl ctl)
{
slru_entry(ctl)->m_blocks_exists += 1;
}
void
pgstat_count_slru_page_read(SlruCtl ctl)
{
slru_entry(ctl)->m_blocks_read += 1;
}
void
pgstat_count_slru_page_written(SlruCtl ctl)
{
slru_entry(ctl)->m_blocks_written += 1;
}
void
pgstat_count_slru_flush(SlruCtl ctl)
{
slru_entry(ctl)->m_flush += 1;
}
void
pgstat_count_slru_truncate(SlruCtl ctl)
{
slru_entry(ctl)->m_truncate += 1;
}

View File

@ -1690,6 +1690,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)
{
@ -1935,6 +2012,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)
{

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202004011
#define CATALOG_VERSION_NO 202004021
#endif

View File

@ -5436,6 +5436,16 @@
proname => 'pg_stat_get_buf_alloc', provolatile => 's', proparallel => 'r',
prorettype => 'int8', proargtypes => '', prosrc => 'pg_stat_get_buf_alloc' },
{ oid => '2306',
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',
@ -5540,6 +5550,11 @@
proname => 'pg_stat_reset_single_function_counters', provolatile => 'v',
prorettype => 'void', proargtypes => 'oid',
prosrc => 'pg_stat_reset_single_function_counters' },
{ oid => '2307',
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',

View File

@ -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;
@ -713,6 +746,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
@ -1210,6 +1258,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
*/
@ -1247,6 +1300,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,
@ -1422,5 +1476,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_count_slru_page_zeroed(SlruCtl ctl);
extern void pgstat_count_slru_page_hit(SlruCtl ctl);
extern void pgstat_count_slru_page_read(SlruCtl ctl);
extern void pgstat_count_slru_page_written(SlruCtl ctl);
extern void pgstat_count_slru_page_exists(SlruCtl ctl);
extern void pgstat_count_slru_flush(SlruCtl ctl);
extern void pgstat_count_slru_truncate(SlruCtl ctl);
extern char *pgstat_slru_name(int idx);
extern int pgstat_slru_index(const char *name);
#endif /* PGSTAT_H */

View File

@ -2007,6 +2007,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,