From 10c65fe527734e3c427ef7dd206e5c5173d21cd7 Mon Sep 17 00:00:00 2001 From: Hari Babu Date: Tue, 5 Sep 2017 11:17:47 +1000 Subject: [PATCH] pg_stat_walwrites statistics view This view will provide the details of how many blocks, writes and the time taken to write the WAL data to disk by the backend, walwriter and other background process. Based on this data, it is configure walwriter to reduce the load on the backend processes, thus it should improve the performance. --- doc/src/sgml/monitoring.sgml | 86 +++++++++++++++++++++++ src/backend/access/transam/xlog.c | 69 ++++++++++++++++++ src/backend/catalog/system_views.sql | 3 + src/backend/postmaster/autovacuum.c | 1 - src/backend/postmaster/pgstat.c | 123 ++++++++++++++++++++++++++++++++- src/backend/postmaster/walwriter.c | 2 + src/backend/utils/adt/pgstatfuncs.c | 36 ++++++++++ src/backend/utils/init/globals.c | 1 + src/include/catalog/pg_proc.h | 3 + src/include/miscadmin.h | 2 + src/include/pgstat.h | 53 +++++++++++++- src/test/regress/expected/rules.out | 10 +++ src/test/regress/expected/sysviews.out | 7 ++ src/test/regress/sql/sysviews.sql | 3 + 14 files changed, 395 insertions(+), 4 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 18fb9c2aa6..7d0b098a36 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -364,6 +364,14 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser + + pg_stat_walwritespg_stat_walwrites + One row only, showing statistics about the + WAL writing activity. See + for details. + + + pg_stat_databasepg_stat_database One row per database, showing database-wide statistics. See @@ -2262,6 +2270,82 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i single row, containing global data for the cluster. + + <structname>pg_stat_walwrites</structname> View + + + + + Column + Type + Description + + + + + + writes + bigint + + Number of WAL writes that are carried out by background processes and workers. + + + + walwriter_writes + bigint + Number of WAL writes that are carried out by the wal writer process + + + backend_writes + bigint + Number of WAL writes that are carried out by the backend processes + + + dirty_writes + bigint + + Number of dirty WAL writes that are carried out by background processes and workers + when the are full. + + + + backend_dirty_writes + bigint + + Number of dirty WAL writes that are carried out by the backend processes when + the are full. + + + + write_blocks + bigint + Number of WAL pages written to the disk by the background processes/workers + + + walwriter_write_blocks + bigint + Number of WAL pages written to the disk by the wal writer process + + + backend_write_blocks + bigint + Number of WAL pages written to the disk by the backend processes + + + stats_reset + timestamp with time zone + Time at which these statistics were last reset + + + +
+ + + The pg_stat_walwrites view will always have a + single row, containing data about the WAL writing activity of the cluster. + + + <structname>pg_stat_database</structname> View @@ -3041,6 +3125,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i counters shown in the pg_stat_bgwriter view. Calling pg_stat_reset_shared('archiver') will zero all the counters shown in the pg_stat_archiver view. + Calling pg_stat_reset_shared('walwrites') will zero all the + counters shown in the pg_stat_walwrites view. diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index dd028a12a4..c3e6f9eb54 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -600,6 +600,9 @@ typedef struct XLogCtlData */ XLogwrtResult LogwrtResult; + /* Protected by WALWriteLock */ + PgStat_WalWritesCounts stats; + /* * Latest initialized page in the cache (last byte position + 1). * @@ -866,6 +869,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void); static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); +static bool am_background_process(void); static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible); static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno, @@ -2118,6 +2122,17 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) WriteRqst.Write = OldPageRqstPtr; WriteRqst.Flush = 0; XLogWrite(WriteRqst, false); + if (AmWalWriterProcess()) + { + /* + * Don't consider the writes of wal writer process as dirty writes, + * so skipping. + */ + } + else if (am_background_process()) + XLogCtl->stats.dirty_writes++; + else + XLogCtl->stats.backend_dirty_writes++; LWLockRelease(WALWriteLock); TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE(); } @@ -2331,6 +2346,33 @@ XLogCheckpointNeeded(XLogSegNo new_segno) return false; } +/* + * Check whether the current process is a background process/worker + * or not. This function checks for the background processes that + * does some WAL write activity only and other background processes + * are not considered. It considers all the background workers + * as WAL write activity workers. + * + * Returns false - when the current process is a normal backend + * true - when the current process a background process/worker + */ +static bool +am_background_process() +{ + /* check whether current process is a background process/worker? */ + if (!AmBackgroundWriterProcess() && + !AmCheckpointerProcess() && + !AmStartupProcess() && + !IsBackgroundWorker && + !am_walsender && + !am_autovacuum_worker) + { + return false; + } + + return true; +} + /* * Write and/or fsync the log at least as far as WriteRqst indicates. * @@ -2354,6 +2396,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) int npages; int startidx; uint32 startoffset; + PgStat_Counter writes = 0; + PgStat_Counter write_blocks = 0; + bool is_background_process = am_background_process(); /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2475,6 +2520,7 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) /* OK to write the page(s) */ from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ; nbytes = npages * (Size) XLOG_BLCKSZ; + nleft = nbytes; do { @@ -2497,6 +2543,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) from += written; } while (nleft > 0); + /* check whether writer is a normal backend or not? */ + writes++; + write_blocks += npages; + /* Update state for write */ openLogOff += nbytes; npages = 0; @@ -2596,6 +2646,22 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Flush = LogwrtResult.Write; } + if (is_background_process) + { + XLogCtl->stats.writes += writes; + XLogCtl->stats.write_blocks += write_blocks; + } + else if (AmWalWriterProcess()) + { + XLogCtl->stats.walwriter_writes += writes; + XLogCtl->stats.walwriter_write_blocks += write_blocks; + } + else + { + XLogCtl->stats.backend_writes += writes; + XLogCtl->stats.backend_write_blocks += write_blocks; + } + /* * Update shared-memory status * @@ -3063,6 +3129,9 @@ XLogBackgroundFlush(void) { XLogWrite(WriteRqst, flexible); } + + /* Collect all the wal write shared counters into local, and report it to stats collector */ + memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts)); LWLockRelease(WALWriteLock); END_CRIT_SECTION(); diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index dc40cde424..08139a14c5 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -881,6 +881,9 @@ CREATE VIEW pg_stat_bgwriter AS pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; +CREATE VIEW pg_stat_walwrites AS + SELECT * FROM pg_stat_get_walwrites() AS A; + CREATE VIEW pg_stat_progress_vacuum AS SELECT S.pid AS pid, S.datid AS datid, D.datname AS datname, diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index db6d91ffdf..18db3aef13 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -135,7 +135,6 @@ int Log_autovacuum_min_duration = -1; /* Flags to tell if we are in an autovacuum process */ static bool am_autovacuum_launcher = false; -static bool am_autovacuum_worker = false; /* Flags set by signal handlers */ static volatile sig_atomic_t got_SIGHUP = false; diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 3a0b49c7c4..0fc34ae946 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -142,6 +142,15 @@ char *pgstat_stat_tmpname = NULL; */ PgStat_MsgBgWriter BgWriterStats; +/* + * WalWrites Local statistics counters. + * The statistics data gets populated in XLogWrite function. + * Stored directly in a stats message structure so it can be sent + * to stats collector process without needing to copy things around. + * We assume this inits to zeroes. + */ +PgStat_MsgWalWrites LocalWalWritesStats; + /* ---------- * Local data * ---------- @@ -256,6 +265,7 @@ static int localNumBackends = 0; */ static PgStat_ArchiverStats archiverStats; static PgStat_GlobalStats globalStats; +static PgStat_WalWritesStats walwritesStats; /* * List of OIDs of databases we need to write out. If an entry is InvalidOid, @@ -336,6 +346,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len); static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len); static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len); static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len); +static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len); /* ------------------------------------------------------------ * Public functions called from postmaster follow @@ -1336,11 +1347,13 @@ 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, "walwrites") == 0) + msg.m_resettarget = RESET_WALWRITES; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized reset target: \"%s\"", target), - errhint("Target must be \"archiver\" or \"bgwriter\"."))); + errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\"."))); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER); pgstat_send(&msg, sizeof(msg)); @@ -2580,6 +2593,21 @@ pgstat_fetch_global(void) return &globalStats; } +/* + * --------- + * pgstat_fetch_stat_walwrites() - + * + * Support function for the SQL-callable pgstat* functions. Returns + * a pointer to the walwrites statistics struct. + * --------- + */ +PgStat_WalWritesStats * +pgstat_fetch_stat_walwrites(void) +{ + backend_read_statsfile(); + + return &walwritesStats; +} /* ------------------------------------------------------------ * Functions for management of the shared-memory PgBackendStatus array @@ -4184,6 +4212,39 @@ pgstat_send_bgwriter(void) MemSet(&BgWriterStats, 0, sizeof(BgWriterStats)); } +/* ---------- + * pgstat_send_walwrites() - + * + * Send wal writes statistics to the collector + * ---------- + */ + +void +pgstat_send_walwrites(void) +{ + /* We assume this initializes to zeroes */ + static const PgStat_MsgWalWrites all_zeroes; + + /* + * 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(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0) + return; + + /* + * Prepare and send the message + */ + pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES); + pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats)); + + /* + * Clear out the statistics buffer, so it can be re-used. + */ + MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats)); +} + /* ---------- * PgstatCollectorMain() - @@ -4400,6 +4461,10 @@ PgstatCollectorMain(int argc, char *argv[]) pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len); break; + case PGSTAT_MTYPE_WALWRITES: + pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len); + break; + default: break; } @@ -4665,6 +4730,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 wal writes stats struct + */ + rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout); + (void) rc; /* we'll check for error with ferror */ + /* * Walk through the database table. */ @@ -4922,6 +4993,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) */ memset(&globalStats, 0, sizeof(globalStats)); memset(&archiverStats, 0, sizeof(archiverStats)); + memset(&walwritesStats, 0, sizeof(walwritesStats)); /* * Set the current timestamp (will be kept only in case we can't load an @@ -4929,6 +5001,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) */ globalStats.stat_reset_timestamp = GetCurrentTimestamp(); archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp; + walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp; /* * Try to open the stats file. If it doesn't exist, the backends simply @@ -4992,6 +5065,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) goto done; } + /* + * Read wal writes stats struct + */ + if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats)) + { + ereport(pgStatRunningInCollector ? LOG : WARNING, + (errmsg("corrupted statistics file \"%s\"", statfile))); + goto done; + } + /* * We found an existing collector stats file. Read it and put all the * hashtable entries into place. @@ -5290,6 +5373,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent, PgStat_StatDBEntry dbentry; PgStat_GlobalStats myGlobalStats; PgStat_ArchiverStats myArchiverStats; + PgStat_WalWritesStats myWalwritesStats; FILE *fpin; int32 format_id; const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename; @@ -5344,6 +5428,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent, return false; } + /* + * Read wal writes stats struct + */ + if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats), + fpin) != sizeof(myWalwritesStats)) + { + 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; @@ -5906,6 +6002,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len) memset(&archiverStats, 0, sizeof(archiverStats)); archiverStats.stat_reset_timestamp = GetCurrentTimestamp(); } + else if (msg->m_resettarget == RESET_WALWRITES) + { + /* Reset the wal writes statistics of the cluster. */ + memset(&walwritesStats, 0, sizeof(walwritesStats)); + walwritesStats.stat_reset_timestamp = GetCurrentTimestamp(); + } /* * Presumably the sender of this message validated the target, don't @@ -6085,6 +6187,25 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len) globalStats.buf_alloc += msg->m_buf_alloc; } +/* ---------- + * pgstat_recv_walwrites() - + * + * Process a WALWRITES message. + * ---------- + */ +static void +pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len) +{ + walwritesStats.stats.writes += msg->stats.writes; + walwritesStats.stats.walwriter_writes += msg->stats.walwriter_writes; + walwritesStats.stats.backend_writes += msg->stats.backend_writes; + walwritesStats.stats.dirty_writes += msg->stats.dirty_writes; + walwritesStats.stats.backend_dirty_writes += msg->stats.backend_dirty_writes; + walwritesStats.stats.write_blocks += msg->stats.write_blocks; + walwritesStats.stats.walwriter_write_blocks += msg->stats.walwriter_write_blocks; + walwritesStats.stats.backend_write_blocks += msg->stats.backend_write_blocks; +} + /* ---------- * pgstat_recv_recoveryconflict() - * diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c index 7b89e02428..4f4dd849f8 100644 --- a/src/backend/postmaster/walwriter.c +++ b/src/backend/postmaster/walwriter.c @@ -280,6 +280,8 @@ WalWriterMain(void) else if (left_till_hibernate > 0) left_till_hibernate--; + pgstat_send_walwrites(); + /* * Sleep until we are signaled or WalWriterDelay has elapsed. If we * haven't done anything useful for quite some time, lengthen the diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 5a968e3758..ad6c886656 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -1870,3 +1870,39 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS) PG_RETURN_DATUM(HeapTupleGetDatum( heap_form_tuple(tupdesc, values, nulls))); } + +Datum +pg_stat_get_walwrites(PG_FUNCTION_ARGS) +{ + TupleDesc tupdesc; +#define NUM_PG_STAT_WALWRITE_COLS 9 + Datum values[NUM_PG_STAT_WALWRITE_COLS]; + bool nulls[NUM_PG_STAT_WALWRITE_COLS]; + PgStat_WalWritesStats *walwrite_stats; + + /* Initialize values and NULL flags arrays */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + /* 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"); + + /* Get statistics about the archiver process */ + walwrite_stats = pgstat_fetch_stat_walwrites(); + + /* Fill values and NULLs */ + values[0] = Int64GetDatum(walwrite_stats->stats.writes); + values[1] = Int64GetDatum(walwrite_stats->stats.walwriter_writes); + values[2] = Int64GetDatum(walwrite_stats->stats.backend_writes); + values[3] = Int64GetDatum(walwrite_stats->stats.dirty_writes); + values[4] = Int64GetDatum(walwrite_stats->stats.backend_dirty_writes); + values[5] = Int64GetDatum(walwrite_stats->stats.write_blocks); + values[6] = Int64GetDatum(walwrite_stats->stats.walwriter_write_blocks); + values[7] = Int64GetDatum(walwrite_stats->stats.backend_write_blocks); + values[8] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp); + + /* Returns the record as Datum */ + PG_RETURN_DATUM(HeapTupleGetDatum( + heap_form_tuple(tupdesc, values, nulls))); +} diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index 7c09498dc0..3cb2b5456f 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -101,6 +101,7 @@ bool IsPostmasterEnvironment = false; bool IsUnderPostmaster = false; bool IsBinaryUpgrade = false; bool IsBackgroundWorker = false; +bool am_autovacuum_worker = false; bool ExitOnAnyError = false; diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 93c031aad7..27205f3c9d 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2980,6 +2980,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync"); DATA(insert OID = 2859 ( pg_stat_get_buf_alloc PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ )); DESCR("statistics: number of buffer allocations"); +DATA(insert OID = 3998 ( pg_stat_get_walwrites PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,20,20,1184}" "{o,o,o,o,o,o,o,o,o}" "{writes,walwriter_writes,backend_writes,dirty_writes,backend_dirty_writes,write_blocks,walwriter_write_blocks,backend_write_blocks,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ )); +DESCR("statistics: information about WAL writes activity"); + DATA(insert OID = 2978 ( pg_stat_get_function_calls PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ )); DESCR("statistics: number of function calls"); DATA(insert OID = 2979 ( pg_stat_get_function_total_time PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ )); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index dad98de98d..201c8e6217 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -243,6 +243,8 @@ extern PGDLLIMPORT int work_mem; extern PGDLLIMPORT int maintenance_work_mem; extern PGDLLIMPORT int replacement_sort_tuples; +extern bool am_autovacuum_worker; + extern int VacuumCostPageHit; extern int VacuumCostPageMiss; extern int VacuumCostPageDirty; diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 089b7c3a10..5be0b062ca 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -64,7 +64,8 @@ typedef enum StatMsgType PGSTAT_MTYPE_FUNCPURGE, PGSTAT_MTYPE_RECOVERYCONFLICT, PGSTAT_MTYPE_TEMPFILE, - PGSTAT_MTYPE_DEADLOCK + PGSTAT_MTYPE_DEADLOCK, + PGSTAT_MTYPE_WALWRITES } StatMsgType; /* ---------- @@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts typedef enum PgStat_Shared_Reset_Target { RESET_ARCHIVER, - RESET_BGWRITER + RESET_BGWRITER, + RESET_WALWRITES } PgStat_Shared_Reset_Target; /* Possible object types for resetting single counters */ @@ -422,6 +424,36 @@ typedef struct PgStat_MsgBgWriter PgStat_Counter m_checkpoint_sync_time; } PgStat_MsgBgWriter; +/* + * Walwrites statistics counters + */ +typedef struct PgStat_WalWritesCounts +{ + PgStat_Counter writes; /* No of writes by background + * processes/workers */ + PgStat_Counter walwriter_writes; /* No of writes by walwriter */ + PgStat_Counter backend_writes; /* No of writes by backends */ + PgStat_Counter dirty_writes;/* No of dirty writes by background + * processes/workers when WAL buffers full */ + PgStat_Counter backend_dirty_writes; /* No of dirty writes by + * backends when WAL buffers + * full */ + PgStat_Counter write_blocks;/* Total no of pages written by background + * processes/workers */ + PgStat_Counter walwriter_write_blocks;/* Total no of pages written by walwriter */ + PgStat_Counter backend_write_blocks; /* Total no of pages written by backends */ +} PgStat_WalWritesCounts; + +/* ---------- + * PgStat_MsgWalWrites Sent by the walwriter after collecting all shared stats + * ---------- + */ +typedef struct PgStat_MsgWalWrites +{ + PgStat_MsgHdr m_hdr; + PgStat_WalWritesCounts stats; +} PgStat_MsgWalWrites; + /* ---------- * PgStat_MsgRecoveryConflict Sent by the backend upon recovery conflict * ---------- @@ -555,6 +587,7 @@ typedef union PgStat_Msg PgStat_MsgFuncpurge msg_funcpurge; PgStat_MsgRecoveryConflict msg_recoveryconflict; PgStat_MsgDeadlock msg_deadlock; + PgStat_MsgWalWrites msg_walwrites; } PgStat_Msg; @@ -694,6 +727,14 @@ typedef struct PgStat_GlobalStats TimestampTz stat_reset_timestamp; } PgStat_GlobalStats; +/* + * Walwrites statistics kept in the stats collector + */ +typedef struct PgStat_WalWritesStats +{ + PgStat_WalWritesCounts stats; + TimestampTz stat_reset_timestamp; /* Last time when the stats reset */ +} PgStat_WalWritesStats; /* ---------- * Backend types @@ -1125,6 +1166,11 @@ extern char *pgstat_stat_filename; */ extern PgStat_MsgBgWriter BgWriterStats; +/* + * Wal writes statistics updated in XLogWrite function + */ +extern PgStat_MsgWalWrites LocalWalWritesStats; + /* * Updated by pgstat_count_buffer_*_time macros */ @@ -1321,6 +1367,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info, extern void pgstat_send_archiver(const char *xlog, bool failed); extern void pgstat_send_bgwriter(void); +extern void pgstat_send_walwrites(void); /* ---------- * Support functions for the SQL-callable functions to @@ -1335,5 +1382,7 @@ 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_WalWritesStats *pgstat_fetch_stat_walwrites(void); + #endif /* PGSTAT_H */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index f1c1b44d6f..6a84e3f980 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1973,6 +1973,16 @@ pg_stat_wal_receiver| SELECT s.pid, s.conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, received_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, conninfo) WHERE (s.pid IS NOT NULL); +pg_stat_walwrites| SELECT a.writes, + a.walwriter_writes, + a.backend_writes, + a.dirty_writes, + a.backend_dirty_writes, + a.write_blocks, + a.walwriter_write_blocks, + a.backend_write_blocks, + a.stats_reset + FROM pg_stat_get_walwrites() a(writes, walwriter_writes, backend_writes, dirty_writes, backend_dirty_writes, write_blocks, walwriter_write_blocks, backend_write_blocks, stats_reset); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out index 568b783f5e..aa91df0a6c 100644 --- a/src/test/regress/expected/sysviews.out +++ b/src/test/regress/expected/sysviews.out @@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts; t (1 row) +-- There will surely and maximum one record +select count(*) = 1 as ok from pg_stat_walwrites; + ok +---- + t +(1 row) + -- This is to record the prevailing planner enable_foo settings during -- a regression test run. select name, setting from pg_settings where name like 'enable%'; diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql index 28e412b735..21f49c9a3b 100644 --- a/src/test/regress/sql/sysviews.sql +++ b/src/test/regress/sql/sysviews.sql @@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements; -- See also prepared_xacts.sql select count(*) >= 0 as ok from pg_prepared_xacts; +-- There will surely and maximum one record +select count(*) = 1 as ok from pg_stat_walwrites; + -- This is to record the prevailing planner enable_foo settings during -- a regression test run. select name, setting from pg_settings where name like 'enable%'; -- 2.14.1.windows.1