Hello,

Attached patch implements the following TODO item :

Track number of WAL files ready to be archived in pg_stat_archiver

However, it will track the total number of any file ready to be
archived, not only WAL files.

Please let me know what you think about it.

Regards.
-- 
Julien Rouhaud
http://dalibo.com - http://dalibo.org
*** a/doc/src/sgml/monitoring.sgml
--- b/doc/src/sgml/monitoring.sgml
***************
*** 728,733 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 728,738 ----
        <entry>Time of the last failed archival operation</entry>
       </row>
       <row>
+       <entry><structfield>ready_count</></entry>
+       <entry><type>bigint</type></entry>
+       <entry>Number of files waiting to be archived</entry>
+      </row>
+      <row>
        <entry><structfield>stats_reset</></entry>
        <entry><type>timestamp with time zone</type></entry>
        <entry>Time at which these statistics were last reset</entry>
*** a/src/backend/access/transam/xlogarchive.c
--- b/src/backend/access/transam/xlogarchive.c
***************
*** 24,29 ****
--- 24,30 ----
  #include "access/xlog_internal.h"
  #include "miscadmin.h"
  #include "postmaster/startup.h"
+ #include "pgstat.h"
  #include "replication/walsender.h"
  #include "storage/fd.h"
  #include "storage/ipc.h"
***************
*** 539,544 **** XLogArchiveNotify(const char *xlog)
--- 540,548 ----
  	/* Notify archiver that it's got something to do */
  	if (IsUnderPostmaster)
  		SendPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER);
+ 
+ 	/* Tell the collector about a new file waiting to be archived */
+ 	pgstat_send_archiver(xlog, ARCH_READY);
  }
  
  /*
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 697,702 **** CREATE VIEW pg_stat_archiver AS
--- 697,703 ----
          s.failed_count,
          s.last_failed_wal,
          s.last_failed_time,
+         s.ready_count,
          s.stats_reset
      FROM pg_stat_get_archiver() s;
  
*** a/src/backend/postmaster/pgarch.c
--- b/src/backend/postmaster/pgarch.c
***************
*** 491,497 **** pgarch_ArchiverCopyLoop(void)
  				 * Tell the collector about the WAL file that we successfully
  				 * archived
  				 */
! 				pgstat_send_archiver(xlog, false);
  
  				break;			/* out of inner retry loop */
  			}
--- 491,497 ----
  				 * Tell the collector about the WAL file that we successfully
  				 * archived
  				 */
! 				pgstat_send_archiver(xlog, ARCH_SUCCESS);
  
  				break;			/* out of inner retry loop */
  			}
***************
*** 501,507 **** pgarch_ArchiverCopyLoop(void)
  				 * Tell the collector about the WAL file that we failed to
  				 * archive
  				 */
! 				pgstat_send_archiver(xlog, true);
  
  				if (++failures >= NUM_ARCHIVE_RETRIES)
  				{
--- 501,507 ----
  				 * Tell the collector about the WAL file that we failed to
  				 * archive
  				 */
! 				pgstat_send_archiver(xlog, ARCH_FAIL);
  
  				if (++failures >= NUM_ARCHIVE_RETRIES)
  				{
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 36,41 ****
--- 36,42 ----
  #include "access/transam.h"
  #include "access/twophase_rmgr.h"
  #include "access/xact.h"
+ #include "access/xlog_internal.h"
  #include "catalog/pg_database.h"
  #include "catalog/pg_proc.h"
  #include "lib/ilist.h"
***************
*** 3084,3094 **** pgstat_send(void *msg, int len)
   * 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;
  
--- 3085,3096 ----
   * pgstat_send_archiver() -
   *
   *	Tell the collector about the WAL file that we successfully
!  *	archived or failed to archive, or the new file waiting
!  *	to be archived.
   * ----------
   */
  void
! pgstat_send_archiver(const char *xlog, ArchiverReason reason)
  {
  	PgStat_MsgArchiver msg;
  
***************
*** 3096,3102 **** pgstat_send_archiver(const char *xlog, bool failed)
  	 * 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));
--- 3098,3104 ----
  	 * Prepare and send the message
  	 */
  	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ARCHIVER);
! 	msg.m_reason = reason;
  	strncpy(msg.m_xlog, xlog, sizeof(msg.m_xlog));
  	msg.m_timestamp = GetCurrentTimestamp();
  	pgstat_send(&msg, sizeof(msg));
***************
*** 3921,3927 **** pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
  	/*
  	 * Try to open the stats file. If it doesn't exist, the backends simply
  	 * return zero for anything and the collector simply starts from scratch
! 	 * with empty counters.
  	 *
  	 * ENOENT is a possibility if the stats collector is not running or has
  	 * not yet written the stats file the first time.  Any other failure
--- 3923,3930 ----
  	/*
  	 * Try to open the stats file. If it doesn't exist, the backends simply
  	 * return zero for anything and the collector simply starts from scratch
! 	 * with empty counters, except for the .ready files count which should
! 	 * always give the real number of files.
  	 *
  	 * ENOENT is a possibility if the stats collector is not running or has
  	 * not yet written the stats file the first time.  Any other failure
***************
*** 3934,3939 **** pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
--- 3937,3970 ----
  					(errcode_for_file_access(),
  					 errmsg("could not open statistics file \"%s\": %m",
  							statfile)));
+ 
+ 		/* Initialize the archive ready counter */
+ 		char		XLogArchiveStatusDir[MAXPGPATH];
+ 		DIR		   *rldir;
+ 		struct dirent *rlde;
+ 
+ 		snprintf(XLogArchiveStatusDir, MAXPGPATH, XLOGDIR "/archive_status");
+ 		rldir = AllocateDir(XLogArchiveStatusDir);
+ 		if (rldir == NULL)
+ 			ereport(ERROR,
+ 					(errcode_for_file_access(),
+ 					 errmsg("could not open archive status directory \"%s\": %m",
+ 							XLogArchiveStatusDir)));
+ 
+ 		while ((rlde = ReadDir(rldir, XLogArchiveStatusDir)) != NULL)
+ 		{
+ 			int		basenamelen = (int) strlen(rlde->d_name) - 6;
+ 
+ 			if (basenamelen >= MIN_XFN_CHARS &&
+ 				basenamelen <= MAX_XFN_CHARS &&
+ 				strspn(rlde->d_name, VALID_XFN_CHARS) >= basenamelen &&
+ 				strcmp(rlde->d_name + basenamelen, ".ready") == 0)
+ 			{
+ 				++archiverStats.ready_count;
+ 			}
+ 		}
+ 		FreeDir(rldir);
+ 
  		return dbhash;
  	}
  
***************
*** 4842,4849 **** pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
  	}
  	else if (msg->m_resettarget == RESET_ARCHIVER)
  	{
! 		/* Reset the archiver statistics for the cluster. */
  		memset(&archiverStats, 0, sizeof(archiverStats));
  		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
  	}
  
--- 4873,4887 ----
  	}
  	else if (msg->m_resettarget == RESET_ARCHIVER)
  	{
! 		PgStat_Counter ready_count;
! 		/*
! 		 * Reset the archiver statistics for the cluster.
! 		 * We must keep the ready_count value as it should
! 		 * always reflect the real count.
! 		*/
! 		ready_count = archiverStats.ready_count;
  		memset(&archiverStats, 0, sizeof(archiverStats));
+ 		archiverStats.ready_count = ready_count;
  		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
  	}
  
***************
*** 4984,5004 **** pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
  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;
  	}
  }
  
--- 5022,5048 ----
  static void
  pgstat_recv_archiver(PgStat_MsgArchiver *msg, int len)
  {
! 	switch (msg->m_reason)
  	{
! 		case ARCH_FAIL:
! 			/* 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;
! 			break;
! 		case ARCH_SUCCESS:
! 			/* 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;
! 			--archiverStats.ready_count;
! 		break;
! 		case ARCH_READY:
! 			/* New file waiting to be archived */
! 			++archiverStats.ready_count;
! 			break;
  	}
  }
  
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
***************
*** 1746,1752 **** pg_stat_get_archiver(PG_FUNCTION_ARGS)
  	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",
--- 1746,1752 ----
  	MemSet(nulls, 0, sizeof(nulls));
  
  	/* Initialise attributes information in the tuple descriptor */
! 	tupdesc = CreateTemplateTupleDesc(8, false);
  	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "archived_count",
  					   INT8OID, -1, 0);
  	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "last_archived_wal",
***************
*** 1759,1765 **** pg_stat_get_archiver(PG_FUNCTION_ARGS)
  					   TEXTOID, -1, 0);
  	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "last_failed_time",
  					   TIMESTAMPTZOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "stats_reset",
  					   TIMESTAMPTZOID, -1, 0);
  
  	BlessTupleDesc(tupdesc);
--- 1759,1767 ----
  					   TEXTOID, -1, 0);
  	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "last_failed_time",
  					   TIMESTAMPTZOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "ready_count",
! 					   INT8OID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "stats_reset",
  					   TIMESTAMPTZOID, -1, 0);
  
  	BlessTupleDesc(tupdesc);
***************
*** 1790,1799 **** pg_stat_get_archiver(PG_FUNCTION_ARGS)
  	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(
--- 1792,1802 ----
  	else
  		values[5] = TimestampTzGetDatum(archiver_stats->last_failed_timestamp);
  
+ 	values[6] = Int64GetDatum(archiver_stats->ready_count);
  	if (archiver_stats->stat_reset_timestamp == 0)
! 		nulls[7] = true;
  	else
! 		values[7] = TimestampTzGetDatum(archiver_stats->stat_reset_timestamp);
  
  	/* Returns the record as Datum */
  	PG_RETURN_DATUM(HeapTupleGetDatum(
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2751,2757 **** DATA(insert OID = 2844 (  pg_stat_get_db_blk_read_time	PGNSP PGUID 12 1 0 0 0 f
  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");
--- 2751,2757 ----
  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,20,1184}" "{o,o,o,o,o,o,o,o}" "{archived_count,last_archived_wal,last_archived_time,failed_count,last_failed_wal,last_failed_time,ready_count,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");
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 376,382 **** typedef struct PgStat_MsgAnalyze
  typedef struct PgStat_MsgArchiver
  {
  	PgStat_MsgHdr m_hdr;
! 	bool		m_failed;		/* Failed attempt */
  	char		m_xlog[MAX_XFN_CHARS + 1];
  	TimestampTz m_timestamp;
  } PgStat_MsgArchiver;
--- 376,382 ----
  typedef struct PgStat_MsgArchiver
  {
  	PgStat_MsgHdr m_hdr;
! 	int			m_reason;
  	char		m_xlog[MAX_XFN_CHARS + 1];
  	TimestampTz m_timestamp;
  } PgStat_MsgArchiver;
***************
*** 651,656 **** typedef struct PgStat_ArchiverStats
--- 651,657 ----
  	char		last_failed_wal[MAX_XFN_CHARS + 1];		/* WAL file involved in
  														 * last failure */
  	TimestampTz last_failed_timestamp;	/* last archival failure time */
+ 	PgStat_Counter ready_count;		/* Number of files waiting to be archived */
  	TimestampTz stat_reset_timestamp;
  } PgStat_ArchiverStats;
  
***************
*** 690,695 **** typedef enum BackendState
--- 691,707 ----
  } BackendState;
  
  /* ----------
+  * Archiver reason
+  * ----------
+  */
+ typedef enum ArchiverReason
+ {
+ 	ARCH_SUCCESS,
+ 	ARCH_FAIL,
+ 	ARCH_READY,
+ } ArchiverReason;
+ 
+ /* ----------
   * Shared-memory data structures
   * ----------
   */
***************
*** 934,940 **** extern void pgstat_twophase_postcommit(TransactionId xid, uint16 info,
  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);
  
  /* ----------
--- 946,952 ----
  extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
  						  void *recdata, uint32 len);
  
! extern void pgstat_send_archiver(const char *xlog, ArchiverReason reason);
  extern void pgstat_send_bgwriter(void);
  
  /* ----------
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1659,1666 **** pg_stat_archiver| SELECT s.archived_count,
      s.failed_count,
      s.last_failed_wal,
      s.last_failed_time,
      s.stats_reset
!    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_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,
--- 1659,1667 ----
      s.failed_count,
      s.last_failed_wal,
      s.last_failed_time,
+     s.ready_count,
      s.stats_reset
!    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, ready_count, 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,
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to