Hi, Attached is v3 of the patch with one big change and various small ones.
The main change is that it gets rid of the SlruType enum and the new field in SlruCtlData. Instead, the patch now uses the name passed to SimpleLruInit (which is then stored as LWLock tranche name). The counters are still stored in a fixed-sized array, and there's a simple name/index mapping. We don't have any registry of stable SLRU IDs, so I can't think of anything better, and I think this is good enough for now. The other change is that I got rid of the io_error counter. We don't have that for shared buffers etc. either, anyway. I've also renamed the colunms from "pages" to "blks" to make it consistent with other similar stats (blks_hit, blks_read). I've renamed the fields to "blks_written" and "blks_zeroed". And finally, I've added the view to monitoring.sgml. Barring objections, I'll get this committed in the next few days, after reviewing the comments a bit. regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>From e1a187b9b331798c87900f94aa77999f9198f556 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <t...@fuzzy.cz> Date: Thu, 26 Mar 2020 20:52:26 +0100 Subject: [PATCH] SLRU stats --- doc/src/sgml/monitoring.sgml | 77 +++++++++ src/backend/access/transam/slru.c | 23 +++ src/backend/catalog/system_views.sql | 13 ++ src/backend/postmaster/pgstat.c | 238 +++++++++++++++++++++++++++ src/backend/utils/adt/pgstatfuncs.c | 77 +++++++++ src/include/catalog/pg_proc.dat | 10 ++ src/include/pgstat.h | 53 +++++- src/test/regress/expected/rules.out | 10 ++ 8 files changed, 500 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 270178d57e..b58ac5acb8 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 diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c index 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.sql index 5a6dc61630..7dba85dd07 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, diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 4763c24be9..895efb4cd2 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -141,6 +141,25 @@ 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 *)) +static int slru_index(char *name); + +/* entries in the same order as slru_names */ +PgStat_MsgSLRU SLRUStats[SLRU_NUM_ELEMENTS]; + /* ---------- * Local data * ---------- @@ -255,6 +274,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 +317,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); @@ -324,6 +345,7 @@ 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(); } /* @@ -1337,6 +1362,8 @@ pgstat_reset_shared_counters(const char *target) msg.m_resettarget = RESET_ARCHIVER; else if (strcmp(target, "bgwriter") == 0) msg.m_resettarget = RESET_BGWRITER; + else if (strcmp(target, "slru") == 0) + msg.m_resettarget = RESET_SLRU; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -2622,6 +2649,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 +4369,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() - @@ -4513,6 +4597,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 +4869,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 +5110,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 +5133,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 +5142,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 +5211,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 +5520,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 +5576,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; @@ -6022,6 +6153,17 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len) memset(&archiverStats, 0, sizeof(archiverStats)); archiverStats.stat_reset_timestamp = GetCurrentTimestamp(); } + else if (msg->m_resettarget == RESET_SLRU) + { + int i; + TimestampTz ts = GetCurrentTimestamp(); + + /* Reset the SLRU statistics for the cluster. */ + memset(&slruStats, 0, sizeof(slruStats)); + + for (i = 0; i < SLRU_NUM_ELEMENTS; i++) + slruStats[i].stat_reset_timestamp = ts; + } /* * Presumably the sender of this message validated the target, don't @@ -6201,6 +6343,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 +6615,81 @@ pgstat_clip_activity(const char *raw_activity) return activity; } + +static int +slru_index(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); +} + +char * +pgstat_slru_name(int idx) +{ + Assert(idx >= 0); + + if (idx >= SLRU_NUM_ELEMENTS) + return NULL; + + return slru_names[idx]; +} + +static PgStat_MsgSLRU * +slru_entry(SlruCtl ctl) +{ + int idx = 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.c index cea01534a5..bc87c093d6 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) { diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 87d25d4a4b..7e4184c7dc 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', diff --git a/src/include/pgstat.h b/src/include/pgstat.h index a07012bf4b..5bf68a0a2a 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" @@ -60,6 +61,7 @@ typedef enum StatMsgType PGSTAT_MTYPE_ANALYZE, PGSTAT_MTYPE_ARCHIVER, PGSTAT_MTYPE_BGWRITER, + PGSTAT_MTYPE_SLRU, PGSTAT_MTYPE_FUNCSTAT, PGSTAT_MTYPE_FUNCPURGE, PGSTAT_MTYPE_RECOVERYCONFLICT, @@ -120,7 +122,8 @@ typedef struct PgStat_TableCounts typedef enum PgStat_Shared_Reset_Target { RESET_ARCHIVER, - RESET_BGWRITER + RESET_BGWRITER, + RESET_SLRU } PgStat_Shared_Reset_Target; /* Possible object types for resetting single counters */ @@ -423,6 +426,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 * ---------- @@ -565,6 +585,7 @@ typedef union PgStat_Msg 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 +733,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 +1245,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 */ @@ -1421,5 +1462,15 @@ 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); #endif /* PGSTAT_H */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 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