From f2c52eeba919a1b191f60445001371bd7c53aaa9 Mon Sep 17 00:00:00 2001 From: Michael Paquier <michael@paquier.xyz> Date: Thu, 2 Dec 2021 13:52:16 +0900 Subject: [PATCH] pg_waldump: Emit stats summary when interrupted by SIGINT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 | 9 ++++++ src/bin/pg_waldump/pg_waldump.c | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/doc/src/sgml/ref/pg_waldump.sgml b/doc/src/sgml/ref/pg_waldump.sgml index 432254d2d5..5735a161ce 100644 --- a/doc/src/sgml/ref/pg_waldump.sgml +++ b/doc/src/sgml/ref/pg_waldump.sgml @@ -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> diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c index 4690e0f515..f66b5a8dba 100644 --- a/src/bin/pg_waldump/pg_waldump.c +++ b/src/bin/pg_waldump/pg_waldump.c @@ -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),