1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Add contrib/pg_walinspect.

Provides similar functionality to pg_waldump, but from a SQL interface
rather than a separate utility.

Author: Bharath Rupireddy
Reviewed-by: Greg Stark, Kyotaro Horiguchi, Andres Freund, Ashutosh Sharma, Nitin Jadhav, RKN Sai Krishna
Discussion: https://postgr.es/m/CALj2ACUGUYXsEQdKhEdsBzhGEyF3xggvLdD8C0VT72TNEfOiog%40mail.gmail.com
This commit is contained in:
Jeff Davis
2022-04-08 00:02:10 -07:00
parent 708007dced
commit 2258e76f90
25 changed files with 1675 additions and 204 deletions

View File

@@ -200,3 +200,133 @@ xlog_identify(uint8 info)
return id;
}
/*
* Returns a string giving information about all the blocks in an
* XLogRecord.
*/
void
XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
bool detailed_format, StringInfo buf,
uint32 *fpi_len)
{
int block_id;
Assert(record != NULL);
if (detailed_format && pretty)
appendStringInfoChar(buf, '\n');
for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
{
RelFileNode rnode = {InvalidOid, InvalidOid, InvalidOid};
ForkNumber forknum = InvalidForkNumber;
BlockNumber blk = InvalidBlockNumber;
if (!XLogRecHasBlockRef(record, block_id))
continue;
XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
if (detailed_format)
{
/* Get block references in detailed format. */
if (pretty)
appendStringInfoChar(buf, '\t');
else if (block_id > 0)
appendStringInfoChar(buf, ' ');
appendStringInfo(buf,
"blkref #%d: rel %u/%u/%u fork %s blk %u",
block_id,
rnode.spcNode, rnode.dbNode, rnode.relNode,
forkNames[forknum],
blk);
if (XLogRecHasBlockImage(record, block_id))
{
uint8 bimg_info = XLogRecGetBlock(record, block_id)->bimg_info;
/* Calculate the amount of FPI data in the record. */
if (fpi_len)
*fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
if (BKPIMAGE_COMPRESSED(bimg_info))
{
const char *method;
if ((bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
method = "pglz";
else if ((bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
method = "lz4";
else if ((bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0)
method = "zstd";
else
method = "unknown";
appendStringInfo(buf,
" (FPW%s); hole: offset: %u, length: %u, "
"compression saved: %u, method: %s",
XLogRecBlockImageApply(record, block_id) ?
"" : " for WAL verification",
XLogRecGetBlock(record, block_id)->hole_offset,
XLogRecGetBlock(record, block_id)->hole_length,
BLCKSZ -
XLogRecGetBlock(record, block_id)->hole_length -
XLogRecGetBlock(record, block_id)->bimg_len,
method);
}
else
{
appendStringInfo(buf,
" (FPW%s); hole: offset: %u, length: %u",
XLogRecBlockImageApply(record, block_id) ?
"" : " for WAL verification",
XLogRecGetBlock(record, block_id)->hole_offset,
XLogRecGetBlock(record, block_id)->hole_length);
}
}
if (pretty)
appendStringInfoChar(buf, '\n');
}
else
{
/* Get block references in short format. */
if (forknum != MAIN_FORKNUM)
{
appendStringInfo(buf,
", blkref #%d: rel %u/%u/%u fork %s blk %u",
block_id,
rnode.spcNode, rnode.dbNode, rnode.relNode,
forkNames[forknum],
blk);
}
else
{
appendStringInfo(buf,
", blkref #%d: rel %u/%u/%u blk %u",
block_id,
rnode.spcNode, rnode.dbNode, rnode.relNode,
blk);
}
if (XLogRecHasBlockImage(record, block_id))
{
/* Calculate the amount of FPI data in the record. */
if (fpi_len)
*fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
if (XLogRecBlockImageApply(record, block_id))
appendStringInfo(buf, " FPW");
else
appendStringInfo(buf, " FPW for WAL verification");
}
}
}
if (!detailed_format && pretty)
appendStringInfoChar(buf, '\n');
}

View File

@@ -34,6 +34,7 @@ OBJS = \
xlogprefetcher.o \
xlogreader.o \
xlogrecovery.o \
xlogstats.o \
xlogutils.o
include $(top_srcdir)/src/backend/common.mk

View File

@@ -1320,13 +1320,6 @@ XLogReaderValidatePageHeader(XLogReaderState *state, XLogRecPtr recptr,
return true;
}
#ifdef FRONTEND
/*
* Functions that are currently not needed in the backend, but are better
* implemented inside xlogreader.c because of the internal facilities available
* here.
*/
/*
* Find the first record with an lsn >= RecPtr.
*
@@ -1447,8 +1440,6 @@ err:
return InvalidXLogRecPtr;
}
#endif /* FRONTEND */
/*
* Helper function to ease writing of XLogRoutine->page_read callbacks.
* If this function is used, caller must supply a segment_open callback in

View File

@@ -0,0 +1,93 @@
/*-------------------------------------------------------------------------
*
* xlogstats.c
* Functions for WAL Statitstics
*
* Copyright (c) 2022, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/backend/access/transam/xlogstats.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xlogreader.h"
#include "access/xlogstats.h"
/*
* Calculate the size of a record, split into !FPI and FPI parts.
*/
void
XLogRecGetLen(XLogReaderState *record, uint32 *rec_len,
uint32 *fpi_len)
{
int block_id;
/*
* Calculate the amount of FPI data in the record.
*
* XXX: We peek into xlogreader's private decoded backup blocks for the
* bimg_len indicating the length of FPI data.
*/
*fpi_len = 0;
for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
{
if (XLogRecHasBlockImage(record, block_id))
*fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
}
/*
* Calculate the length of the record as the total length - the length of
* all the block images.
*/
*rec_len = XLogRecGetTotalLen(record) - *fpi_len;
}
/*
* Store per-rmgr and per-record statistics for a given record.
*/
void
XLogRecStoreStats(XLogStats *stats, XLogReaderState *record)
{
RmgrId rmid;
uint8 recid;
uint32 rec_len;
uint32 fpi_len;
Assert(stats != NULL && record != NULL);
stats->count++;
rmid = XLogRecGetRmid(record);
XLogRecGetLen(record, &rec_len, &fpi_len);
/* Update per-rmgr statistics */
stats->rmgr_stats[rmid].count++;
stats->rmgr_stats[rmid].rec_len += rec_len;
stats->rmgr_stats[rmid].fpi_len += fpi_len;
/*
* Update per-record statistics, where the record is identified by a
* combination of the RmgrId and the four bits of the xl_info field that
* are the rmgr's domain (resulting in sixteen possible entries per
* RmgrId).
*/
recid = XLogRecGetInfo(record) >> 4;
/*
* XACT records need to be handled differently. Those records use the
* first bit of those four bits for an optional flag variable and the
* following three bits for the opcode. We filter opcode out of xl_info
* and use it as the identifier of the record.
*/
if (rmid == RM_XACT_ID)
recid &= 0x07;
stats->record_stats[rmid][recid].count++;
stats->record_stats[rmid][recid].rec_len += rec_len;
stats->record_stats[rmid][recid].fpi_len += fpi_len;
}

View File

@@ -80,6 +80,10 @@ typedef struct xl_invalid_page
static HTAB *invalid_page_tab = NULL;
static int
read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
int reqLen, XLogRecPtr targetRecPtr,
char *cur_page, bool wait_for_wal);
/* Report a reference to an invalid page */
static void
@@ -870,6 +874,31 @@ wal_segment_close(XLogReaderState *state)
int
read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
{
return read_local_xlog_page_guts(state, targetPagePtr, reqLen,
targetRecPtr, cur_page, true);
}
/*
* Same as read_local_xlog_page except that it doesn't wait for future WAL
* to be available.
*/
int
read_local_xlog_page_no_wait(XLogReaderState *state, XLogRecPtr targetPagePtr,
int reqLen, XLogRecPtr targetRecPtr,
char *cur_page)
{
return read_local_xlog_page_guts(state, targetPagePtr, reqLen,
targetRecPtr, cur_page, false);
}
/*
* Implementation of read_local_xlog_page and its no wait version.
*/
static int
read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
int reqLen, XLogRecPtr targetRecPtr,
char *cur_page, bool wait_for_wal)
{
XLogRecPtr read_upto,
loc;
@@ -925,6 +954,10 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
if (loc <= read_upto)
break;
/* If asked, let's not wait for future WAL. */
if (!wait_for_wal)
break;
CHECK_FOR_INTERRUPTS();
pg_usleep(1000L);
}