*** a/doc/src/sgml/monitoring.sgml --- b/doc/src/sgml/monitoring.sgml *************** *** 270,275 **** postgres: user database host + pg_stat_archiverpg_stat_archiver + One row only, showing statistics about the + WAL archiver process's activity. See + for details. + + + + pg_stat_bgwriterpg_stat_bgwriter One row only, showing statistics about the background writer process's activity. See *************** *** 648,653 **** postgres: user database host + + <structname>pg_stat_archiver</structname> View + + + + + Column + Type + Description + + + + + + archived_count + bigint + Number of WAL files that have been successfully archived + + + last_archived_wal + text + Name of the last WAL file successfully archived + + + last_archived_time + timestamp with time zone + Time of the last successful archive operation + + + failed_count + bigint + Number of failed attempts for archiving WAL files + + + last_failed_wal + text + Name of the WAL file of the last failed archival operation + + + last_failed_time + timestamp with time zone + Time of the last failed archival operation + + + stats_reset + timestamp with time zone + Time at which these statistics were last reset + + + +
+ + + The pg_stat_archiver view will always have a + single row, containing data about the archiver process of the cluster. + + <structname>pg_stat_bgwriter</structname> View *************** *** 1613,1618 **** postgres: user database host pg_stat_reset_shared('bgwriter') will zero all the 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. *** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** *** 672,677 **** CREATE VIEW pg_stat_xact_user_functions AS --- 672,688 ---- WHERE P.prolang != 12 -- fast check to eliminate built-in functions AND pg_stat_get_xact_function_calls(P.oid) IS NOT NULL; + CREATE VIEW pg_stat_archiver AS + SELECT + s.archived_count, + s.last_archived_wal, + s.last_archived_time, + s.failed_count, + s.last_failed_wal, + s.last_failed_time, + s.stats_reset + FROM pg_stat_get_archiver() s; + CREATE VIEW pg_stat_bgwriter AS SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, *** a/src/backend/postmaster/pgarch.c --- b/src/backend/postmaster/pgarch.c *************** *** 36,41 **** --- 36,42 ---- #include "access/xlog_internal.h" #include "libpq/pqsignal.h" #include "miscadmin.h" + #include "pgstat.h" #include "postmaster/fork_process.h" #include "postmaster/pgarch.h" #include "postmaster/postmaster.h" *************** *** 496,505 **** pgarch_ArchiverCopyLoop(void) --- 497,513 ---- { /* successful */ pgarch_archiveDone(xlog); + + /* Tell the collector about the WAL file that we successfully archived */ + pgstat_send_archiver(xlog, false); + break; /* out of inner retry loop */ } else { + /* Tell the collector about the WAL file that we failed to archive */ + pgstat_send_archiver(xlog, true); + if (++failures >= NUM_ARCHIVE_RETRIES) { ereport(WARNING, *** a/src/backend/postmaster/pgstat.c --- b/src/backend/postmaster/pgstat.c *************** *** 221,226 **** static int localNumBackends = 0; --- 221,227 ---- * Contains statistics that are not collected per database * or per table. */ + static PgStat_ArchiverStats archiverStats; static PgStat_GlobalStats globalStats; /* Write request info for each database */ *************** *** 292,297 **** static void pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, in --- 293,299 ---- static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len); static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len); static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len); + static void pgstat_recv_archiver(PgStat_MsgArchiver *msg, int len); static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len); static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len); static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len); *************** *** 1257,1269 **** pgstat_reset_shared_counters(const char *target) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to reset statistics counters"))); ! if (strcmp(target, "bgwriter") == 0) msg.m_resettarget = RESET_BGWRITER; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized reset target: \"%s\"", target), ! errhint("Target must be \"bgwriter\"."))); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER); pgstat_send(&msg, sizeof(msg)); --- 1259,1273 ---- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to reset statistics counters"))); ! if (strcmp(target, "archiver") == 0) ! msg.m_resettarget = RESET_ARCHIVER; ! else if (strcmp(target, "bgwriter") == 0) msg.m_resettarget = RESET_BGWRITER; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized reset target: \"%s\"", target), ! errhint("Target must be \"archiver\" or \"bgwriter\"."))); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER); pgstat_send(&msg, sizeof(msg)); *************** *** 2323,2328 **** pgstat_fetch_stat_numbackends(void) --- 2327,2349 ---- /* * --------- + * pgstat_fetch_stat_archiver() - + * + * Support function for the SQL-callable pgstat* functions. Returns + * a pointer to the archiver statistics struct. + * --------- + */ + PgStat_ArchiverStats * + pgstat_fetch_stat_archiver(void) + { + backend_read_statsfile(); + + return &archiverStats; + } + + + /* + * --------- * pgstat_fetch_global() - * * Support function for the SQL-callable pgstat* functions. Returns *************** *** 3036,3041 **** pgstat_send(void *msg, int len) --- 3057,3084 ---- } /* ---------- + * pgstat_send_archiver() - + * + * Tell the collector about the WAL file that we successfully + * archived or failed to archive. + * ---------- + */ + void + pgstat_send_archiver(const char *xlog, bool failed) + { + PgStat_MsgArchiver msg; + + /* + * Prepare and send the message + */ + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ARCHIVER); + msg.m_failed = failed; + strncpy(msg.m_xlog, xlog, sizeof(msg.m_xlog)); + msg.m_timestamp = GetCurrentTimestamp(); + pgstat_send(&msg, sizeof(msg)); + } + + /* ---------- * pgstat_send_bgwriter() - * * Send bgwriter statistics to the collector *************** *** 3278,3283 **** PgstatCollectorMain(int argc, char *argv[]) --- 3321,3330 ---- pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, len); break; + case PGSTAT_MTYPE_ARCHIVER: + pgstat_recv_archiver((PgStat_MsgArchiver *) &msg, len); + break; + case PGSTAT_MTYPE_BGWRITER: pgstat_recv_bgwriter((PgStat_MsgBgWriter *) &msg, len); break; *************** *** 3563,3568 **** pgstat_write_statsfiles(bool permanent, bool allDbs) --- 3610,3621 ---- (void) rc; /* we'll check for error with ferror */ /* + * Write archiver stats struct + */ + rc = fwrite(&archiverStats, sizeof(archiverStats), 1, fpout); + (void) rc; /* we'll check for error with ferror */ + + /* * Walk through the database table. */ hash_seq_init(&hstat, pgStatDBHash); *************** *** 3828,3843 **** pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); /* ! * Clear out global statistics so they start from zero in case we can't ! * load an existing statsfile. */ memset(&globalStats, 0, sizeof(globalStats)); /* * Set the current timestamp (will be kept only in case we can't load an * existing statsfile). */ globalStats.stat_reset_timestamp = GetCurrentTimestamp(); /* * Try to open the stats file. If it doesn't exist, the backends simply --- 3881,3898 ---- HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); /* ! * Clear out global and archiver statistics so they start from zero ! * in case we can't load an existing statsfile. */ memset(&globalStats, 0, sizeof(globalStats)); + memset(&archiverStats, 0, sizeof(archiverStats)); /* * Set the current timestamp (will be kept only in case we can't load an * existing statsfile). */ globalStats.stat_reset_timestamp = GetCurrentTimestamp(); + archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp; /* * Try to open the stats file. If it doesn't exist, the backends simply *************** *** 3875,3881 **** pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) if (fread(&globalStats, 1, sizeof(globalStats), fpin) != sizeof(globalStats)) { ereport(pgStatRunningInCollector ? LOG : WARNING, ! (errmsg("corrupted statistics file \"%s\"", statfile))); goto done; } --- 3930,3946 ---- if (fread(&globalStats, 1, sizeof(globalStats), fpin) != sizeof(globalStats)) { ereport(pgStatRunningInCollector ? LOG : WARNING, ! (errmsg("corrupted statistics file (global) \"%s\"", statfile))); ! goto done; ! } ! ! /* ! * Read archiver stats struct ! */ ! if (fread(&archiverStats, 1, sizeof(archiverStats), fpin) != sizeof(archiverStats)) ! { ! ereport(pgStatRunningInCollector ? LOG : WARNING, ! (errmsg("corrupted statistics file (archiver) \"%s\"", statfile))); goto done; } *************** *** 4159,4165 **** done: * stats_timestamp value. * * - if there's no db stat entry (e.g. for a new or inactive database), ! * there's no stat_timestamp value, but also nothing to write so we return * the timestamp of the global statfile. * ---------- */ --- 4224,4230 ---- * stats_timestamp value. * * - if there's no db stat entry (e.g. for a new or inactive database), ! * there's no stats_timestamp value, but also nothing to write so we return * the timestamp of the global statfile. * ---------- */ *************** *** 4169,4174 **** pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent, --- 4234,4240 ---- { PgStat_StatDBEntry dbentry; PgStat_GlobalStats myGlobalStats; + PgStat_ArchiverStats myArchiverStats; FILE *fpin; int32 format_id; const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename; *************** *** 4211,4216 **** pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent, --- 4277,4294 ---- return false; } + /* + * Read archiver stats struct + */ + if (fread(&myArchiverStats, 1, sizeof(myArchiverStats), + fpin) != sizeof(myArchiverStats)) + { + 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; *************** *** 4738,4743 **** pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len) --- 4816,4827 ---- memset(&globalStats, 0, sizeof(globalStats)); globalStats.stat_reset_timestamp = GetCurrentTimestamp(); } + else if (msg->m_resettarget == RESET_ARCHIVER) + { + /* Reset the archiver statistics for the cluster. */ + memset(&archiverStats, 0, sizeof(archiverStats)); + archiverStats.stat_reset_timestamp = GetCurrentTimestamp(); + } /* * Presumably the sender of this message validated the target, don't *************** *** 4868,4873 **** pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len) --- 4952,4984 ---- /* ---------- + * pgstat_recv_archiver() - + * + * Process a ARCHIVER message. + * ---------- + */ + static void + pgstat_recv_archiver(PgStat_MsgArchiver *msg, int len) + { + if (msg->m_failed) + { + /* Failed archival attempt */ + ++archiverStats.failed_count; + memcpy(archiverStats.last_failed_wal, msg->m_xlog, + sizeof(archiverStats.last_failed_wal)); + archiverStats.last_failed_timestamp = msg->m_timestamp; + } + else + { + /* Successful archival operation */ + ++archiverStats.archived_count; + memcpy(archiverStats.last_archived_wal, msg->m_xlog, + sizeof(archiverStats.last_archived_wal)); + archiverStats.last_archived_timestamp = msg->m_timestamp; + } + } + + /* ---------- * pgstat_recv_bgwriter() - * * Process a BGWRITER message. *** a/src/backend/utils/adt/pgstatfuncs.c --- b/src/backend/utils/adt/pgstatfuncs.c *************** *** 87,92 **** extern Datum pg_stat_get_db_temp_bytes(PG_FUNCTION_ARGS); --- 87,94 ---- extern Datum pg_stat_get_db_blk_read_time(PG_FUNCTION_ARGS); extern Datum pg_stat_get_db_blk_write_time(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_archiver(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_bgwriter_timed_checkpoints(PG_FUNCTION_ARGS); extern Datum pg_stat_get_bgwriter_requested_checkpoints(PG_FUNCTION_ARGS); extern Datum pg_stat_get_checkpoint_write_time(PG_FUNCTION_ARGS); *************** *** 1712,1714 **** pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS) --- 1714,1783 ---- PG_RETURN_VOID(); } + + Datum + pg_stat_get_archiver(PG_FUNCTION_ARGS) + { + TupleDesc tupdesc; + Datum values[7]; + bool nulls[7]; + PgStat_ArchiverStats *archiver_stats; + + /* Initialise values and NULL flags arrays */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + /* Initialise attributes information in the tuple descriptor */ + tupdesc = CreateTemplateTupleDesc(7, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "archived_count", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "last_archived_wal", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "last_archived_time", + TIMESTAMPTZOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "failed_count", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "last_failed_wal", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "last_failed_time", + TIMESTAMPTZOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "stats_reset", + TIMESTAMPTZOID, -1, 0); + + BlessTupleDesc(tupdesc); + + /* Get statistics about the archiver process */ + archiver_stats = pgstat_fetch_stat_archiver(); + + /* Fill values and NULLs */ + values[0] = Int64GetDatum(archiver_stats->archived_count); + if (archiver_stats->last_archived_wal == 0) + nulls[1] = true; + else + values[1] = CStringGetTextDatum(archiver_stats->last_archived_wal); + + if (archiver_stats->last_archived_timestamp == 0) + nulls[2] = true; + else + values[2] = TimestampTzGetDatum(archiver_stats->last_archived_timestamp); + + values[3] = Int64GetDatum(archiver_stats->failed_count); + if (archiver_stats->last_failed_wal == 0) + nulls[4] = true; + else + values[4] = CStringGetTextDatum(archiver_stats->last_failed_wal); + + if (archiver_stats->last_failed_timestamp == 0) + nulls[5] = true; + else + values[5] = TimestampTzGetDatum(archiver_stats->last_failed_timestamp); + + if (archiver_stats->stat_reset_timestamp == 0) + nulls[6] = true; + else + values[6] = TimestampTzGetDatum(archiver_stats->stat_reset_timestamp); + + /* Returns the record as Datum */ + PG_RETURN_DATUM(HeapTupleGetDatum( + heap_form_tuple(tupdesc, values, nulls))); + } *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 2702,2707 **** DATA(insert OID = 2844 ( pg_stat_get_db_blk_read_time PGNSP PGUID 12 1 0 0 0 f --- 2702,2709 ---- DESCR("statistics: block read time, in msec"); DATA(insert OID = 2845 ( pg_stat_get_db_blk_write_time PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 701 "26" _null_ _null_ _null_ _null_ pg_stat_get_db_blk_write_time _null_ _null_ _null_ )); DESCR("statistics: block write time, in msec"); + DATA(insert OID = 3195 ( pg_stat_get_archiver PGNSP PGUID 12 1 0 0 0 f f f f f f s 0 0 2249 "" "{20,25,1184,20,25,1184,1184}" "{o,o,o,o,o,o,o}" "{archived_count,last_archived_wal,last_archived_time,failed_count,last_failed_wal,last_failed_time,stats_reset}" _null_ pg_stat_get_archiver _null_ _null_ _null_ )); + DESCR("statistics: information about WAL archiver"); DATA(insert OID = 2769 ( pg_stat_get_bgwriter_timed_checkpoints PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_bgwriter_timed_checkpoints _null_ _null_ _null_ )); DESCR("statistics: number of timed checkpoints started by the bgwriter"); DATA(insert OID = 2770 ( pg_stat_get_bgwriter_requested_checkpoints PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_bgwriter_requested_checkpoints _null_ _null_ _null_ )); *** a/src/include/pgstat.h --- b/src/include/pgstat.h *************** *** 17,22 **** --- 17,23 ---- #include "portability/instr_time.h" #include "utils/hsearch.h" #include "utils/relcache.h" + #include "access/xlog_internal.h" /* Values for track_functions GUC variable --- order is significant! */ *************** *** 44,49 **** typedef enum StatMsgType --- 45,51 ---- PGSTAT_MTYPE_AUTOVAC_START, PGSTAT_MTYPE_VACUUM, PGSTAT_MTYPE_ANALYZE, + PGSTAT_MTYPE_ARCHIVER, PGSTAT_MTYPE_BGWRITER, PGSTAT_MTYPE_FUNCSTAT, PGSTAT_MTYPE_FUNCPURGE, *************** *** 102,107 **** typedef struct PgStat_TableCounts --- 104,110 ---- /* Possible targets for resetting cluster-wide shared values */ typedef enum PgStat_Shared_Reset_Target { + RESET_ARCHIVER, RESET_BGWRITER } PgStat_Shared_Reset_Target; *************** *** 356,361 **** typedef struct PgStat_MsgAnalyze --- 359,376 ---- /* ---------- + * PgStat_MsgArchiver Sent by the archiver to update statistics. + * ---------- + */ + typedef struct PgStat_MsgArchiver + { + PgStat_MsgHdr m_hdr; + bool m_failed; /* Failed attempt */ + char m_xlog[MAXFNAMELEN]; + TimestampTz m_timestamp; + } PgStat_MsgArchiver; + + /* ---------- * PgStat_MsgBgWriter Sent by the bgwriter to update statistics. * ---------- */ *************** *** 502,507 **** typedef union PgStat_Msg --- 517,523 ---- PgStat_MsgAutovacStart msg_autovacuum; PgStat_MsgVacuum msg_vacuum; PgStat_MsgAnalyze msg_analyze; + PgStat_MsgArchiver msg_archiver; PgStat_MsgBgWriter msg_bgwriter; PgStat_MsgFuncstat msg_funcstat; PgStat_MsgFuncpurge msg_funcpurge; *************** *** 518,524 **** typedef union PgStat_Msg * ------------------------------------------------------------ */ ! #define PGSTAT_FILE_FORMAT_ID 0x01A5BC9B /* ---------- * PgStat_StatDBEntry The collector's data per database --- 534,540 ---- * ------------------------------------------------------------ */ ! #define PGSTAT_FILE_FORMAT_ID 0x01A5BC9C /* ---------- * PgStat_StatDBEntry The collector's data per database *************** *** 612,617 **** typedef struct PgStat_StatFuncEntry --- 628,647 ---- /* + * Archiver statistics kept in the stats collector + */ + typedef struct PgStat_ArchiverStats + { + PgStat_Counter archived_count; /* archival successes */ + char last_archived_wal[MAXFNAMELEN]; /* last WAL file archived */ + TimestampTz last_archived_timestamp; /* last archival success time */ + PgStat_Counter failed_count; /* failed archival attempts */ + char last_failed_wal[MAXFNAMELEN]; /* WAL file involved in last failure */ + TimestampTz last_failed_timestamp; /* last archival failure time */ + TimestampTz stat_reset_timestamp; + } PgStat_ArchiverStats; + + /* * Global statistics kept in the stats collector */ typedef struct PgStat_GlobalStats *************** *** 863,868 **** extern void pgstat_twophase_postcommit(TransactionId xid, uint16 info, --- 893,899 ---- extern void pgstat_twophase_postabort(TransactionId xid, uint16 info, void *recdata, uint32 len); + extern void pgstat_send_archiver(const char *xlog, bool failed); extern void pgstat_send_bgwriter(void); /* ---------- *************** *** 875,880 **** extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid); --- 906,912 ---- extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid); 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); #endif /* PGSTAT_H */ *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** *** 1640,1645 **** pg_stat_all_tables| SELECT c.oid AS relid, --- 1640,1653 ---- LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname; + pg_stat_archiver| SELECT s.archived_wals, + s.last_archived_wal, + s.last_archived_wal_time, + s.failed_attempts, + s.last_failed_wal, + s.last_failed_wal_time, + s.stats_reset + FROM pg_stat_get_archiver() s(archived_wals, last_archived_wal, last_archived_wal_time, failed_attempts, last_failed_wal, last_failed_wal_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, pg_stat_get_checkpoint_write_time() AS checkpoint_write_time,