diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c index f8e7670f8d..3f45db7ea9 100644 --- a/src/backend/access/transam/clog.c +++ b/src/backend/access/transam/clog.c @@ -692,7 +692,8 @@ CLOGShmemInit(void) { ClogCtl->PagePrecedes = CLOGPagePrecedes; SimpleLruInit(ClogCtl, "clog", CLOGShmemBuffers(), CLOG_LSNS_PER_PAGE, - CLogControlLock, "pg_xact", LWTRANCHE_CLOG_BUFFERS); + CLogControlLock, "pg_xact", LWTRANCHE_CLOG_BUFFERS, + SLRU_CLOG); } /* diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c index 630df672cc..44d7ca4483 100644 --- a/src/backend/access/transam/commit_ts.c +++ b/src/backend/access/transam/commit_ts.c @@ -494,7 +494,7 @@ CommitTsShmemInit(void) CommitTsCtl->PagePrecedes = CommitTsPagePrecedes; SimpleLruInit(CommitTsCtl, "commit_timestamp", CommitTsShmemBuffers(), 0, CommitTsControlLock, "pg_commit_ts", - LWTRANCHE_COMMITTS_BUFFERS); + LWTRANCHE_COMMITTS_BUFFERS, SLRU_COMMIT_TS); commitTsShared = ShmemInitStruct("CommitTs shared", sizeof(CommitTimestampShared), diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 50e98caaeb..37a5854284 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -1831,11 +1831,11 @@ MultiXactShmemInit(void) SimpleLruInit(MultiXactOffsetCtl, "multixact_offset", NUM_MXACTOFFSET_BUFFERS, 0, MultiXactOffsetControlLock, "pg_multixact/offsets", - LWTRANCHE_MXACTOFFSET_BUFFERS); + LWTRANCHE_MXACTOFFSET_BUFFERS, SLRU_MULTIXACT_OFFSET); SimpleLruInit(MultiXactMemberCtl, "multixact_member", NUM_MXACTMEMBER_BUFFERS, 0, MultiXactMemberControlLock, "pg_multixact/members", - LWTRANCHE_MXACTMEMBER_BUFFERS); + LWTRANCHE_MXACTMEMBER_BUFFERS, SLRU_MULTIXACT_MEMBER); /* Initialize our shared state struct */ MultiXactState = ShmemInitStruct("Shared MultiXact State", diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c index d5b7a08f73..dab15e6ab4 100644 --- a/src/backend/access/transam/slru.c +++ b/src/backend/access/transam/slru.c @@ -162,7 +162,8 @@ SimpleLruShmemSize(int nslots, int nlsns) void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, - LWLock *ctllock, const char *subdir, int tranche_id) + LWLock *ctllock, const char *subdir, int tranche_id, + SlruType type) { SlruShared shared; bool found; @@ -247,6 +248,7 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, */ ctl->shared = shared; ctl->do_fsync = true; /* default behavior */ + ctl->type = type; StrNCpy(ctl->Dir, subdir, sizeof(ctl->Dir)); } @@ -286,6 +288,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_zero_page(ctl); + return slotno; } @@ -403,9 +408,16 @@ 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 shared buffers */ + pgstat_count_slru_page_hit(ctl); + return slotno; } + /* update the stats counter of pages not found */ + pgstat_count_slru_page_miss(ctl); + /* We found no match; assert we selected a freeable slot */ Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY || (shared->page_status[slotno] == SLRU_PAGE_VALID && @@ -596,6 +608,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); @@ -650,6 +665,9 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno) char path[MAXPGPATH]; int fd; + /* update the stats counter of read pages */ + pgstat_count_slru_page_read(ctl); + SlruFileName(ctl, path, segno); /* @@ -730,6 +748,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_write(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 @@ -901,6 +922,9 @@ SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid) int offset = rpageno * BLCKSZ; char path[MAXPGPATH]; + /* update the stats counter of errors */ + pgstat_count_slru_io_error(ctl); + SlruFileName(ctl, path, segno); errno = slru_errno; switch (slru_errcause) @@ -1125,6 +1149,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 +1213,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. */ diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c index 25d7d739cf..3316accb50 100644 --- a/src/backend/access/transam/subtrans.c +++ b/src/backend/access/transam/subtrans.c @@ -193,7 +193,7 @@ SUBTRANSShmemInit(void) SubTransCtl->PagePrecedes = SubTransPagePrecedes; SimpleLruInit(SubTransCtl, "subtrans", NUM_SUBTRANS_BUFFERS, 0, SubtransControlLock, "pg_subtrans", - LWTRANCHE_SUBTRANS_BUFFERS); + LWTRANCHE_SUBTRANS_BUFFERS, SLRU_SUBTRANS); /* Override default assumption that writes should be fsync'd */ SubTransCtl->do_fsync = false; } diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index c9e75f4370..1844f56859 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -790,6 +790,20 @@ 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.pages_zero, + s.pages_hit, + s.pages_miss, + s.pages_exists, + s.pages_read, + s.pages_write, + s.io_error, + s.flushes, + s.truncates + FROM pg_stat_get_slru() s; + CREATE VIEW pg_stat_wal_receiver AS SELECT s.pid, diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 9aa2b61600..02222cc92a 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -552,7 +552,8 @@ AsyncShmemInit(void) */ AsyncCtl->PagePrecedes = asyncQueuePagePrecedes; SimpleLruInit(AsyncCtl, "async", NUM_ASYNC_BUFFERS, 0, - AsyncCtlLock, "pg_notify", LWTRANCHE_ASYNC_BUFFERS); + AsyncCtlLock, "pg_notify", LWTRANCHE_ASYNC_BUFFERS, + SLRU_ASYNC); /* Override default assumption that writes should be fsync'd */ AsyncCtl->do_fsync = false; diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 51c486bebd..aa825029b1 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -141,6 +141,9 @@ char *pgstat_stat_tmpname = NULL; */ PgStat_MsgBgWriter BgWriterStats; +/* TODO */ +PgStat_MsgSlru SlruStats[SLRU_OTHER + 1]; + /* ---------- * Local data * ---------- @@ -255,6 +258,7 @@ static int localNumBackends = 0; */ static PgStat_ArchiverStats archiverStats; static PgStat_GlobalStats globalStats; +static PgStat_SlruStats slruStats[SLRU_OTHER + 1]; /* * List of OIDs of databases we need to write out. If an entry is InvalidOid, @@ -297,6 +301,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 +329,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); @@ -904,6 +910,9 @@ pgstat_report_stat(bool force) /* Now, send function statistics */ pgstat_send_funcstats(); + + /* Finally send SLRU statistics */ + pgstat_send_slru(); } /* @@ -2619,6 +2628,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 * ------------------------------------------------------------ @@ -4410,6 +4436,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_OTHER; 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_type = 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() - @@ -4602,6 +4668,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; @@ -4832,6 +4902,7 @@ pgstat_write_statsfiles(bool permanent, bool allDbs) const char *tmpfile = permanent ? PGSTAT_STAT_PERMANENT_TMPFILE : pgstat_stat_tmpname; const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename; int rc; + int i; elog(DEBUG2, "writing stats file \"%s\"", statfile); @@ -4872,6 +4943,15 @@ 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 + */ + for (i = 0; i <= SLRU_OTHER; i++) + { + rc = fwrite(&slruStats[i], sizeof(PgStat_SlruStats), 1, fpout); + (void) rc; /* we'll check for error with ferror */ + } + /* * Walk through the database table. */ @@ -5107,6 +5187,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. @@ -5129,6 +5210,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 @@ -5199,6 +5281,20 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) goto done; } + /* + * Read SLRU stats struct + */ + for (i = 0; i <= SLRU_OTHER; i++) + { + if (fread(&slruStats[i], 1, sizeof(PgStat_SlruStats), fpin) != sizeof(PgStat_SlruStats)) + { + ereport(pgStatRunningInCollector ? LOG : WARNING, + (errmsg("corrupted statistics file \"%s\"", statfile))); + memset(&slruStats[i], 0, sizeof(PgStat_SlruStats)); + goto done; + } + } + /* * We found an existing collector stats file. Read it and put all the * hashtable entries into place. @@ -5497,9 +5593,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 @@ -5551,6 +5649,21 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent, return false; } + /* + * Read SLRU stats struct + */ + for (i = 0; i <= SLRU_OTHER; 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; @@ -6292,6 +6405,26 @@ 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_type].pages_zero += msg->m_pages_zero; + slruStats[msg->m_type].pages_hit += msg->m_pages_hit; + slruStats[msg->m_type].pages_miss += msg->m_pages_miss; + slruStats[msg->m_type].pages_exists += msg->m_pages_exists; + slruStats[msg->m_type].pages_read += msg->m_pages_read; + slruStats[msg->m_type].pages_write += msg->m_pages_write; + slruStats[msg->m_type].io_error += msg->m_io_error; + slruStats[msg->m_type].flush += msg->m_flush; + slruStats[msg->m_type].truncate += msg->m_truncate; +} + /* ---------- * pgstat_recv_recoveryconflict() - * @@ -6546,3 +6679,57 @@ pgstat_clip_activity(const char *raw_activity) return activity; } + +void +pgstat_count_slru_zero_page(SlruCtl ctl) +{ + SlruStats[ctl->type].m_pages_zero += 1; +} + +void +pgstat_count_slru_page_hit(SlruCtl ctl) +{ + SlruStats[ctl->type].m_pages_hit += 1; +} + +void +pgstat_count_slru_page_miss(SlruCtl ctl) +{ + SlruStats[ctl->type].m_pages_miss += 1; +} + +void +pgstat_count_slru_page_exists(SlruCtl ctl) +{ + SlruStats[ctl->type].m_pages_exists += 1; +} + +void +pgstat_count_slru_page_read(SlruCtl ctl) +{ + SlruStats[ctl->type].m_pages_read += 1; +} + +void +pgstat_count_slru_page_write(SlruCtl ctl) +{ + SlruStats[ctl->type].m_pages_write += 1; +} + +void +pgstat_count_slru_io_error(SlruCtl ctl) +{ + SlruStats[ctl->type].m_io_error += 1; +} + +void +pgstat_count_slru_flush(SlruCtl ctl) +{ + SlruStats[ctl->type].m_flush += 1; +} + +void +pgstat_count_slru_truncate(SlruCtl ctl) +{ + SlruStats[ctl->type].m_truncate += 1; +} diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index de46b841cb..82e5d7ec57 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -824,7 +824,7 @@ OldSerXidInit(void) OldSerXidSlruCtl->PagePrecedes = OldSerXidPagePrecedesLogically; SimpleLruInit(OldSerXidSlruCtl, "oldserxid", NUM_OLDSERXID_BUFFERS, 0, OldSerXidLock, "pg_serial", - LWTRANCHE_OLDSERXID_BUFFERS); + LWTRANCHE_OLDSERXID_BUFFERS, SLRU_OLDSERXID); /* Override default assumption that writes should be fsync'd */ OldSerXidSlruCtl->do_fsync = false; diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 74f899f24d..8233568800 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -1665,6 +1665,96 @@ 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 10 + 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 <= SLRU_OTHER; i++) + { + /* for each row */ + Datum values[PG_STAT_GET_SLRU_COLS]; + bool nulls[PG_STAT_GET_SLRU_COLS]; + PgStat_SlruStats stat = stats[i]; + text *name; + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + if (i == SLRU_CLOG) + name = cstring_to_text("clog"); + else if (i == SLRU_COMMIT_TS) + name = cstring_to_text("commit_timestamp"); + else if (i == SLRU_MULTIXACT_OFFSET) + name = cstring_to_text("multixact_offset"); + else if (i == SLRU_MULTIXACT_MEMBER) + name = cstring_to_text("multixact_member"); + else if (i == SLRU_SUBTRANS) + name = cstring_to_text("subtrans"); + else if (i == SLRU_ASYNC) + name = cstring_to_text("async"); + else if (i == SLRU_OLDSERXID) + name = cstring_to_text("oldserxid"); + else if (i == SLRU_OTHER) + name = cstring_to_text("other"); + + values[0] = PointerGetDatum(name); + values[1] = Int64GetDatum(stat.pages_zero); + values[2] = Int64GetDatum(stat.pages_hit); + values[3] = Int64GetDatum(stat.pages_miss); + values[4] = Int64GetDatum(stat.pages_exists); + values[5] = Int64GetDatum(stat.pages_read); + values[6] = Int64GetDatum(stat.pages_write); + values[7] = Int64GetDatum(stat.io_error); + values[8] = Int64GetDatum(stat.flush); + values[9] = Int64GetDatum(stat.truncate); + + 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/access/slru.h b/src/include/access/slru.h index 00dbd803e1..eb79ab346f 100644 --- a/src/include/access/slru.h +++ b/src/include/access/slru.h @@ -106,6 +106,18 @@ typedef struct SlruSharedData typedef SlruSharedData *SlruShared; +typedef enum SlruType +{ + SLRU_CLOG, + SLRU_COMMIT_TS, + SLRU_MULTIXACT_OFFSET, + SLRU_MULTIXACT_MEMBER, + SLRU_SUBTRANS, + SLRU_ASYNC, + SLRU_OLDSERXID, + SLRU_OTHER +} SlruType; + /* * SlruCtlData is an unshared structure that points to the active information * in shared memory. @@ -114,6 +126,9 @@ typedef struct SlruCtlData { SlruShared shared; + /* type of the SLRU */ + SlruType type; + /* * This flag tells whether to fsync writes (true for pg_xact and multixact * stuff, false for pg_subtrans and pg_notify). @@ -139,7 +154,8 @@ typedef SlruCtlData *SlruCtl; extern Size SimpleLruShmemSize(int nslots, int nlsns); extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, - LWLock *ctllock, const char *subdir, int tranche_id); + LWLock *ctllock, const char *subdir, int tranche_id, + SlruType type); extern int SimpleLruZeroPage(SlruCtl ctl, int pageno); extern int SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok, TransactionId xid); diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index fcf2a1214c..eadd68e409 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5408,6 +5408,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,int8,int8}', + proargmodes => '{o,o,o,o,o,o,o,o,o,o}', + proargnames => '{name,pages_zero,pages_hit,pages_miss,pages_exists,pages_read,pages_write,io_error,flushes,truncates}', + 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 36b530bc27..677ce2a87a 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 "port/atomics.h" @@ -59,6 +60,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, @@ -422,6 +424,25 @@ 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_type; + PgStat_Counter m_pages_zero; + PgStat_Counter m_pages_hit; + PgStat_Counter m_pages_miss; + PgStat_Counter m_pages_exists; + PgStat_Counter m_pages_read; + PgStat_Counter m_pages_write; + PgStat_Counter m_io_error; + PgStat_Counter m_flush; + PgStat_Counter m_truncate; +} PgStat_MsgSlru; + /* ---------- * PgStat_MsgRecoveryConflict Sent by the backend upon recovery conflict * ---------- @@ -564,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; @@ -711,6 +733,22 @@ typedef struct PgStat_GlobalStats TimestampTz stat_reset_timestamp; } PgStat_GlobalStats; +/* + * Slru statistics kept in the stats collector + */ +typedef struct PgStat_SlruStats +{ + PgStat_Counter pages_zero; + PgStat_Counter pages_hit; + PgStat_Counter pages_miss; + PgStat_Counter pages_exists; + PgStat_Counter pages_read; + PgStat_Counter pages_write; + PgStat_Counter io_error; + PgStat_Counter flush; + PgStat_Counter truncate; +} PgStat_SlruStats; + /* ---------- * Backend types @@ -1223,6 +1261,11 @@ extern char *pgstat_stat_filename; */ extern PgStat_MsgBgWriter BgWriterStats; +/* + * SLRU statistics counters are updated directly by slru. + */ +extern PgStat_MsgSlru SlruStats[SLRU_OTHER + 1]; + /* * Updated by pgstat_count_buffer_*_time macros */ @@ -1436,5 +1479,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_zero_page(SlruCtl ctl); +extern void pgstat_count_slru_page_hit(SlruCtl ctl); +extern void pgstat_count_slru_page_miss(SlruCtl ctl); +extern void pgstat_count_slru_page_exists(SlruCtl ctl); +extern void pgstat_count_slru_page_read(SlruCtl ctl); +extern void pgstat_count_slru_page_write(SlruCtl ctl); +extern void pgstat_count_slru_io_error(SlruCtl ctl); +extern void pgstat_count_slru_flush(SlruCtl ctl); +extern void pgstat_count_slru_truncate(SlruCtl ctl); #endif /* PGSTAT_H */