stats-slru-v1.patch
text/plain
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v1
| File | + | − |
|---|---|---|
| src/backend/access/transam/clog.c | 2 | 1 |
| src/backend/access/transam/commit_ts.c | 1 | 1 |
| src/backend/access/transam/multixact.c | 2 | 2 |
| src/backend/access/transam/slru.c | 31 | 1 |
| src/backend/access/transam/subtrans.c | 1 | 1 |
| src/backend/catalog/system_views.sql | 14 | 0 |
| src/backend/commands/async.c | 2 | 1 |
| src/backend/postmaster/pgstat.c | 187 | 0 |
| src/backend/storage/lmgr/predicate.c | 1 | 1 |
| src/backend/utils/adt/pgstatfuncs.c | 90 | 0 |
| src/include/access/slru.h | 17 | 1 |
| src/include/catalog/pg_proc.dat | 10 | 0 |
| src/include/pgstat.h | 54 | 0 |
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 */