pg_waldump: Emit stats summary when interrupted by SIGINT
authorMichael Paquier <[email protected]>
Thu, 2 Dec 2021 04:52:16 +0000 (13:52 +0900)
committerMichael Paquier <[email protected]>
Thu, 2 Dec 2021 04:52:16 +0000 (13:52 +0900)
Previously, pg_waldump would not display its statistics summary if it
got interrupted by SIGINT (or say a simple Ctrl+C).  It gains with this
commit a signal handler for SIGINT, trapping the signal to exit at the
earliest convenience to allow a display of the stats summary before
exiting.  This makes the reports more interactive, similarly to strace
-c.

This new behavior makes the combination of the options --stats and
--follow much more useful, so as the user will get a report for any
invocation of pg_waldump in such a case.  Information about the LSN
range of the stats computed is added as a header to the report
displayed.

This implementation comes from a suggestion by Álvaro Herrera and
myself, following a complaint by the author of this patch about --stats
and --follow not being useful together originally.

As documented, this is not supported on Windows, though its support
would be possible by catching the terminal events associated to Ctrl+C,
for example (this may require a more centralized implementation, as
other tools could benefit from a common API).

Author: Bharath Rupireddy
Discussion: https://postgr.es/m/CALj2ACUUx3PcK2z9h0_m7vehreZAUbcmOky9WSEpe8TofhV=PQ@mail.gmail.com

doc/src/sgml/ref/pg_waldump.sgml
src/bin/pg_waldump/pg_waldump.c

index 432254d2d5d19c0cea72324a1bbd342d5e8ec327..5735a161ce0e565563e5bb34e0917ab073f61508 100644 (file)
@@ -202,6 +202,15 @@ PostgreSQL documentation
         full-page images) instead of individual records. Optionally
         generate statistics per-record instead of per-rmgr.
        </para>
+
+       <para>
+        If <application>pg_waldump</application> is terminated by signal
+        <systemitem>SIGINT</systemitem>
+        (<keycombo action="simul"><keycap>Control</keycap><keycap>C</keycap></keycombo>,
+        the summary of the statistics computed is displayed up to the
+        termination point. This operation is not supported on
+        <productname>Windows</productname>.
+       </para>
       </listitem>
      </varlistentry>
 
index 4690e0f51561149095dc344842b18b4c62ee09ff..f66b5a8dba0d73a18783101ef3b1d4509f7a3996 100644 (file)
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include <dirent.h>
+#include <signal.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -28,6 +29,7 @@
 static const char *progname;
 
 static int     WalSegSz;
+static volatile sig_atomic_t time_to_stop = false;
 
 typedef struct XLogDumpPrivate
 {
@@ -67,12 +69,27 @@ typedef struct Stats
 typedef struct XLogDumpStats
 {
        uint64          count;
+       XLogRecPtr      startptr;
+       XLogRecPtr      endptr;
        Stats           rmgr_stats[RM_NEXT_ID];
        Stats           record_stats[RM_NEXT_ID][MAX_XLINFO_TYPES];
 } XLogDumpStats;
 
 #define fatal_error(...) do { pg_log_fatal(__VA_ARGS__); exit(EXIT_FAILURE); } while(0)
 
+/*
+ * When sigint is called, just tell the system to exit at the next possible
+ * moment.
+ */
+#ifndef WIN32
+
+static void
+sigint_handler(int signum)
+{
+       time_to_stop = true;
+}
+#endif
+
 static void
 print_rmgr_list(void)
 {
@@ -632,6 +649,12 @@ XLogDumpDisplayStats(XLogDumpConfig *config, XLogDumpStats *stats)
        double          rec_len_pct,
                                fpi_len_pct;
 
+       /*
+        * Leave if no stats have been computed yet, as tracked by the end LSN.
+        */
+       if (XLogRecPtrIsInvalid(stats->endptr))
+               return;
+
        /*
         * Each row shows its percentages of the total, so make a first pass to
         * calculate column totals.
@@ -645,6 +668,9 @@ XLogDumpDisplayStats(XLogDumpConfig *config, XLogDumpStats *stats)
        }
        total_len = total_rec_len + total_fpi_len;
 
+       printf("WAL statistics between %X/%X and %X/%X:\n",
+                  LSN_FORMAT_ARGS(stats->startptr), LSN_FORMAT_ARGS(stats->endptr));
+
        /*
         * 27 is strlen("Transaction/COMMIT_PREPARED"), 20 is strlen(2^64), 8 is
         * strlen("(100.00%)")
@@ -794,6 +820,10 @@ main(int argc, char **argv)
        int                     option;
        int                     optindex = 0;
 
+#ifndef WIN32
+       pqsignal(SIGINT, sigint_handler);
+#endif
+
        pg_logging_init(argv[0]);
        set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_waldump"));
        progname = get_progname(argv[0]);
@@ -833,6 +863,9 @@ main(int argc, char **argv)
        config.stats = false;
        config.stats_per_record = false;
 
+       stats.startptr = InvalidXLogRecPtr;
+       stats.endptr = InvalidXLogRecPtr;
+
        if (argc <= 1)
        {
                pg_log_error("no arguments specified");
@@ -1084,8 +1117,17 @@ main(int argc, char **argv)
                           LSN_FORMAT_ARGS(first_record),
                           (uint32) (first_record - private.startptr));
 
+       if (config.stats == true && !config.quiet)
+               stats.startptr = first_record;
+
        for (;;)
        {
+               if (time_to_stop)
+               {
+                       /* We've been Ctrl-C'ed, so leave */
+                       break;
+               }
+
                /* try to read the next record */
                record = XLogReadRecord(xlogreader_state, &errormsg);
                if (!record)
@@ -1112,7 +1154,10 @@ main(int argc, char **argv)
                if (!config.quiet)
                {
                        if (config.stats == true)
+                       {
                                XLogDumpCountRecord(&config, &stats, xlogreader_state);
+                               stats.endptr = xlogreader_state->EndRecPtr;
+                       }
                        else
                                XLogDumpDisplayRecord(&config, xlogreader_state);
                }
@@ -1127,6 +1172,9 @@ main(int argc, char **argv)
        if (config.stats == true && !config.quiet)
                XLogDumpDisplayStats(&config, &stats);
 
+       if (time_to_stop)
+               exit(0);
+
        if (errormsg)
                fatal_error("error in WAL record at %X/%X: %s",
                                        LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),