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>
</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
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>
/* 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;
}
}
/* 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;
}
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;
}
}
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);
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
int i;
bool ok;
+ /* update the stats counter of flushes */
+ pgstat_count_slru_flush(ctl);
+
/*
* Find and write dirty pages
*/
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.
*/
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,
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;
*/
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
* ----------
*/
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,
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);
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);
/* Now, send function statistics */
pgstat_send_funcstats();
+
+ /* Finally send SLRU statistics */
+ pgstat_send_slru();
}
/*
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() -
*
}
+/*
+ * ---------
+ * 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
* ------------------------------------------------------------
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() -
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;
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;
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.
*/
int32 format_id;
bool found;
const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
+ int i;
/*
* The tables will live in pgStatLocalContext.
*/
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
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
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.
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;
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;
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() -
*
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() -
*
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;
+}
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)
{
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)
{
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202004011
+#define CATALOG_VERSION_NO 202004021
#endif
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',
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',
#ifndef PGSTAT_H
#define PGSTAT_H
+#include "access/slru.h"
#include "datatype/timestamp.h"
#include "libpq/pqcomm.h"
#include "miscadmin.h"
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,
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
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
* ----------
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;
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
*/
extern PgStat_MsgBgWriter BgWriterStats;
+/*
+ * SLRU statistics counters are updated directly by slru.
+ */
+extern PgStat_MsgSLRU SlruStats[];
+
/*
* Updated by pgstat_count_buffer_*_time macros
*/
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,
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 */
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,