Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit bdd5726

Browse files
committed
Add the capability to display summary statistics to pg_xlogdump.
The new --stats/--stats=record options to pg_xlogdump display per rmgr/per record statistics about the parsed WAL. This is useful to understand what the WAL primarily consists of, to allow targeted optimizations on application, configuration, and core code level. It is likely that we will want to fine tune the statistics further, but the feature already is quite helpful. Author: Abhijit Menon-Sen, slightly editorialized by me Reviewed-By: Andres Freund, Dilip Kumar and Furuya Osamu Discussion: 20140604104716.GA3989@toroid.org
1 parent 728f152 commit bdd5726

File tree

2 files changed

+237
-12
lines changed

2 files changed

+237
-12
lines changed

contrib/pg_xlogdump/pg_xlogdump.c

+225-12
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,31 @@ typedef struct XLogDumpConfig
4141
int stop_after_records;
4242
int already_displayed_records;
4343
bool follow;
44+
bool stats;
45+
bool stats_per_record;
4446

4547
/* filter options */
4648
int filter_by_rmgr;
4749
TransactionId filter_by_xid;
4850
bool filter_by_xid_enabled;
4951
} XLogDumpConfig;
5052

53+
typedef struct Stats
54+
{
55+
uint64 count;
56+
uint64 rec_len;
57+
uint64 fpi_len;
58+
} Stats;
59+
60+
#define MAX_XLINFO_TYPES 16
61+
62+
typedef struct XLogDumpStats
63+
{
64+
uint64 count;
65+
Stats rmgr_stats[RM_NEXT_ID];
66+
Stats record_stats[RM_NEXT_ID][MAX_XLINFO_TYPES];
67+
} XLogDumpStats;
68+
5169
static void
5270
fatal_error(const char *fmt,...)
5371
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
@@ -322,22 +340,49 @@ XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
322340
}
323341

324342
/*
325-
* Print a record to stdout
343+
* Store per-rmgr and per-record statistics for a given record.
326344
*/
327345
static void
328-
XLogDumpDisplayRecord(XLogDumpConfig *config, XLogRecPtr ReadRecPtr, XLogRecord *record)
346+
XLogDumpCountRecord(XLogDumpConfig *config, XLogDumpStats *stats, XLogRecPtr ReadRecPtr, XLogRecord *record)
329347
{
330-
const RmgrDescData *desc = &RmgrDescTable[record->xl_rmid];
348+
RmgrId rmid;
349+
uint8 recid;
350+
351+
stats->count++;
331352

332-
if (config->filter_by_rmgr != -1 &&
333-
config->filter_by_rmgr != record->xl_rmid)
334-
return;
353+
/* Update per-rmgr statistics */
335354

336-
if (config->filter_by_xid_enabled &&
337-
config->filter_by_xid != record->xl_xid)
338-
return;
355+
rmid = record->xl_rmid;
339356

340-
config->already_displayed_records++;
357+
stats->rmgr_stats[rmid].count++;
358+
stats->rmgr_stats[rmid].rec_len +=
359+
record->xl_len + SizeOfXLogRecord;
360+
stats->rmgr_stats[rmid].fpi_len +=
361+
record->xl_tot_len - (record->xl_len + SizeOfXLogRecord);
362+
363+
/*
364+
* Update per-record statistics, where the record is identified by a
365+
* combination of the RmgrId and the four bits of the xl_info field
366+
* that are the rmgr's domain (resulting in sixteen possible entries
367+
* per RmgrId).
368+
*/
369+
370+
recid = record->xl_info >> 4;
371+
372+
stats->record_stats[rmid][recid].count++;
373+
stats->record_stats[rmid][recid].rec_len +=
374+
record->xl_len + SizeOfXLogRecord;
375+
stats->record_stats[rmid][recid].fpi_len +=
376+
record->xl_tot_len - (record->xl_len + SizeOfXLogRecord);
377+
}
378+
379+
/*
380+
* Print a record to stdout
381+
*/
382+
static void
383+
XLogDumpDisplayRecord(XLogDumpConfig *config, XLogRecPtr ReadRecPtr, XLogRecord *record)
384+
{
385+
const RmgrDescData *desc = &RmgrDescTable[record->xl_rmid];
341386

342387
printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, bkp: %u%u%u%u, desc: %s ",
343388
desc->rm_name,
@@ -381,6 +426,134 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogRecPtr ReadRecPtr, XLogRecord
381426
}
382427
}
383428

429+
/*
430+
* Display a single row of record counts and sizes for an rmgr or record.
431+
*/
432+
static void
433+
XLogDumpStatsRow(const char *name,
434+
uint64 n, double n_pct,
435+
uint64 rec_len, double rec_len_pct,
436+
uint64 fpi_len, double fpi_len_pct,
437+
uint64 total_len, double total_len_pct)
438+
{
439+
printf("%-27s "
440+
"%20" INT64_MODIFIER "u (%6.02f) "
441+
"%20" INT64_MODIFIER "u (%6.02f) "
442+
"%20" INT64_MODIFIER "u (%6.02f) "
443+
"%20" INT64_MODIFIER "u (%6.02f)\n",
444+
name, n, n_pct, rec_len, rec_len_pct, fpi_len, fpi_len_pct,
445+
total_len, total_len_pct);
446+
}
447+
448+
449+
/*
450+
* Display summary statistics about the records seen so far.
451+
*/
452+
static void
453+
XLogDumpDisplayStats(XLogDumpConfig *config, XLogDumpStats *stats)
454+
{
455+
int ri, rj;
456+
uint64 total_count = 0;
457+
uint64 total_rec_len = 0;
458+
uint64 total_fpi_len = 0;
459+
uint64 total_len = 0;
460+
461+
/* ---
462+
* Make a first pass to calculate column totals:
463+
* count(*),
464+
* sum(xl_len+SizeOfXLogRecord),
465+
* sum(xl_tot_len-xl_len-SizeOfXLogRecord), and
466+
* sum(xl_tot_len).
467+
* These are used to calculate percentages for each record type.
468+
* ---
469+
*/
470+
471+
for (ri = 0; ri < RM_NEXT_ID; ri++)
472+
{
473+
total_count += stats->rmgr_stats[ri].count;
474+
total_rec_len += stats->rmgr_stats[ri].rec_len;
475+
total_fpi_len += stats->rmgr_stats[ri].fpi_len;
476+
}
477+
total_len = total_rec_len+total_fpi_len;
478+
479+
/*
480+
* 27 is strlen("Transaction/COMMIT_PREPARED"),
481+
* 20 is strlen(2^64), 8 is strlen("(100.00%)")
482+
*/
483+
484+
printf("%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n"
485+
"%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n",
486+
"Type", "N", "(%)", "Record size", "(%)", "FPI size", "(%)", "Combined size", "(%)",
487+
"----", "-", "---", "-----------", "---", "--------", "---", "-------------", "---");
488+
489+
for (ri = 0; ri < RM_NEXT_ID; ri++)
490+
{
491+
uint64 count, rec_len, fpi_len, tot_len;
492+
const RmgrDescData *desc = &RmgrDescTable[ri];
493+
494+
if (!config->stats_per_record)
495+
{
496+
count = stats->rmgr_stats[ri].count;
497+
rec_len = stats->rmgr_stats[ri].rec_len;
498+
fpi_len = stats->rmgr_stats[ri].fpi_len;
499+
tot_len = rec_len + fpi_len;
500+
501+
XLogDumpStatsRow(desc->rm_name,
502+
count, 100 * (double) count / total_count,
503+
rec_len, 100 * (double) rec_len / total_rec_len,
504+
fpi_len, 100 * (double) fpi_len / total_fpi_len,
505+
tot_len, 100 * (double) tot_len / total_len);
506+
}
507+
else
508+
{
509+
for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
510+
{
511+
const char *id;
512+
513+
count = stats->record_stats[ri][rj].count;
514+
rec_len = stats->record_stats[ri][rj].rec_len;
515+
fpi_len = stats->record_stats[ri][rj].fpi_len;
516+
tot_len = rec_len + fpi_len;
517+
518+
/* Skip undefined combinations and ones that didn't occur */
519+
if (count == 0)
520+
continue;
521+
522+
/* the upper four bits in xl_info are the rmgr's */
523+
id = desc->rm_identify(rj << 4);
524+
if (id == NULL)
525+
id = psprintf("UNKNOWN (%x)", rj << 4);
526+
527+
XLogDumpStatsRow(psprintf("%s/%s", desc->rm_name, id),
528+
count, 100 * (double) count / total_count,
529+
rec_len, 100 * (double) rec_len / total_rec_len,
530+
fpi_len, 100 * (double) fpi_len / total_fpi_len,
531+
tot_len, 100 * (double) tot_len / total_len);
532+
}
533+
}
534+
}
535+
536+
printf("%-27s %20s %8s %20s %8s %20s %8s %20s\n",
537+
"", "--------", "", "--------", "", "--------", "", "--------");
538+
539+
/*
540+
* The percentages in earlier rows were calculated against the
541+
* column total, but the ones that follow are against the row total.
542+
* Note that these are displayed with a % symbol to differentiate
543+
* them from the earlier ones, and are thus up to 9 characters long.
544+
*/
545+
546+
printf("%-27s "
547+
"%20" INT64_MODIFIER "u %-9s"
548+
"%20" INT64_MODIFIER "u %-9s"
549+
"%20" INT64_MODIFIER "u %-9s"
550+
"%20" INT64_MODIFIER "u %-6s\n",
551+
"Total", stats->count, "",
552+
total_rec_len, psprintf("[%.02f%%]", 100 * (double)total_rec_len / total_len),
553+
total_fpi_len, psprintf("[%.02f%%]", 100 * (double)total_fpi_len / total_len),
554+
total_len, "[100%]");
555+
}
556+
384557
static void
385558
usage(void)
386559
{
@@ -402,6 +575,8 @@ usage(void)
402575
printf(" (default: 1 or the value used in STARTSEG)\n");
403576
printf(" -V, --version output version information, then exit\n");
404577
printf(" -x, --xid=XID only show records with TransactionId XID\n");
578+
printf(" -z, --stats[=record] show statistics instead of records\n");
579+
printf(" (optionally, show per-record statistics)\n");
405580
printf(" -?, --help show this help, then exit\n");
406581
}
407582

@@ -413,6 +588,7 @@ main(int argc, char **argv)
413588
XLogReaderState *xlogreader_state;
414589
XLogDumpPrivate private;
415590
XLogDumpConfig config;
591+
XLogDumpStats stats;
416592
XLogRecord *record;
417593
XLogRecPtr first_record;
418594
char *errormsg;
@@ -429,6 +605,7 @@ main(int argc, char **argv)
429605
{"timeline", required_argument, NULL, 't'},
430606
{"xid", required_argument, NULL, 'x'},
431607
{"version", no_argument, NULL, 'V'},
608+
{"stats", optional_argument, NULL, 'z'},
432609
{NULL, 0, NULL, 0}
433610
};
434611

@@ -439,6 +616,7 @@ main(int argc, char **argv)
439616

440617
memset(&private, 0, sizeof(XLogDumpPrivate));
441618
memset(&config, 0, sizeof(XLogDumpConfig));
619+
memset(&stats, 0, sizeof(XLogDumpStats));
442620

443621
private.timeline = 1;
444622
private.startptr = InvalidXLogRecPtr;
@@ -452,14 +630,16 @@ main(int argc, char **argv)
452630
config.filter_by_rmgr = -1;
453631
config.filter_by_xid = InvalidTransactionId;
454632
config.filter_by_xid_enabled = false;
633+
config.stats = false;
634+
config.stats_per_record = false;
455635

456636
if (argc <= 1)
457637
{
458638
fprintf(stderr, "%s: no arguments specified\n", progname);
459639
goto bad_argument;
460640
}
461641

462-
while ((option = getopt_long(argc, argv, "be:?fn:p:r:s:t:Vx:",
642+
while ((option = getopt_long(argc, argv, "be:?fn:p:r:s:t:Vx:z",
463643
long_options, &optindex)) != -1)
464644
{
465645
switch (option)
@@ -552,6 +732,21 @@ main(int argc, char **argv)
552732
}
553733
config.filter_by_xid_enabled = true;
554734
break;
735+
case 'z':
736+
config.stats = true;
737+
config.stats_per_record = false;
738+
if (optarg)
739+
{
740+
if (strcmp(optarg, "record") == 0)
741+
config.stats_per_record = true;
742+
else if (strcmp(optarg, "rmgr") != 0)
743+
{
744+
fprintf(stderr, "%s: unrecognised argument to --stats: %s\n",
745+
progname, optarg);
746+
goto bad_argument;
747+
}
748+
}
749+
break;
555750
default:
556751
goto bad_argument;
557752
}
@@ -712,14 +907,32 @@ main(int argc, char **argv)
712907

713908
/* after reading the first record, continue at next one */
714909
first_record = InvalidXLogRecPtr;
715-
XLogDumpDisplayRecord(&config, xlogreader_state->ReadRecPtr, record);
910+
911+
/* apply all specified filters */
912+
if (config.filter_by_rmgr != -1 &&
913+
config.filter_by_rmgr != record->xl_rmid)
914+
continue;
915+
916+
if (config.filter_by_xid_enabled &&
917+
config.filter_by_xid != record->xl_xid)
918+
continue;
919+
920+
/* process the record */
921+
if (config.stats == true)
922+
XLogDumpCountRecord(&config, &stats, xlogreader_state->ReadRecPtr, record);
923+
else
924+
XLogDumpDisplayRecord(&config, xlogreader_state->ReadRecPtr, record);
716925

717926
/* check whether we printed enough */
927+
config.already_displayed_records++;
718928
if (config.stop_after_records > 0 &&
719929
config.already_displayed_records >= config.stop_after_records)
720930
break;
721931
}
722932

933+
if (config.stats == true)
934+
XLogDumpDisplayStats(&config, &stats);
935+
723936
if (errormsg)
724937
fatal_error("error in WAL record at %X/%X: %s\n",
725938
(uint32) (xlogreader_state->ReadRecPtr >> 32),

doc/src/sgml/pg_xlogdump.sgml

+12
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,18 @@ PostgreSQL documentation
179179
</listitem>
180180
</varlistentry>
181181

182+
<varlistentry>
183+
<term><option>-z</option></term>
184+
<term><option>--stats[=record]</option></term>
185+
<listitem>
186+
<para>
187+
Display summary statistics (number and size of records and
188+
full-page images) instead of individual records. Optionally
189+
generate statistics per-record instead of per-rmgr.
190+
</para>
191+
</listitem>
192+
</varlistentry>
193+
182194
<varlistentry>
183195
<term><option>-?</></term>
184196
<term><option>--help</></term>

0 commit comments

Comments
 (0)