On Thu, Sep 29, 2022 at 7:43 PM Bharath Rupireddy <bharath.rupireddyforpostg...@gmail.com> wrote: > > Please see the attached v1 patch.
FWIW, I'm attaching Nathan's patch that introduced the new function pg_walfile_offset_lsn as 0002 in the v1 patch set. Here's the CF entry - https://commitfest.postgresql.org/40/3909/. Please consider this for further review. -- Bharath Rupireddy PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
From b19aa25a0d1f2ce85abe0c2081c0e7ede256e329 Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> Date: Thu, 29 Sep 2022 13:29:44 +0000 Subject: [PATCH v1] Add LSN along with offset to error messages reported for WAL file read/write/validate header failures --- src/backend/access/transam/xlog.c | 8 ++++++-- src/backend/access/transam/xlogreader.c | 16 +++++++++++----- src/backend/access/transam/xlogrecovery.c | 13 ++++++++----- src/backend/access/transam/xlogutils.c | 10 ++++++---- src/include/access/xlogreader.h | 1 + 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 8e15256db8..a495bbac85 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -2218,6 +2218,7 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) { char xlogfname[MAXFNAMELEN]; int save_errno; + XLogRecPtr lsn; if (errno == EINTR) continue; @@ -2226,11 +2227,14 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) XLogFileName(xlogfname, tli, openLogSegNo, wal_segment_size); errno = save_errno; + XLogSegNoOffsetToRecPtr(openLogSegNo, startoffset, + wal_segment_size, lsn); ereport(PANIC, (errcode_for_file_access(), errmsg("could not write to log file %s " - "at offset %u, length %zu: %m", - xlogfname, startoffset, nleft))); + "at offset %u, LSN %X/%X, length %zu: %m", + xlogfname, startoffset, + LSN_FORMAT_ARGS(lsn), nleft))); } nleft -= written; from += written; diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c index 5a8fe81f82..c3befc44ba 100644 --- a/src/backend/access/transam/xlogreader.c +++ b/src/backend/access/transam/xlogreader.c @@ -1229,9 +1229,10 @@ XLogReaderValidatePageHeader(XLogReaderState *state, XLogRecPtr recptr, XLogFileName(fname, state->seg.ws_tli, segno, state->segcxt.ws_segsize); report_invalid_record(state, - "invalid magic number %04X in WAL segment %s, offset %u", + "invalid magic number %04X in WAL segment %s, LSN %X/%X, offset %u", hdr->xlp_magic, fname, + LSN_FORMAT_ARGS(recptr), offset); return false; } @@ -1243,9 +1244,10 @@ XLogReaderValidatePageHeader(XLogReaderState *state, XLogRecPtr recptr, XLogFileName(fname, state->seg.ws_tli, segno, state->segcxt.ws_segsize); report_invalid_record(state, - "invalid info bits %04X in WAL segment %s, offset %u", + "invalid info bits %04X in WAL segment %s, LSN %X/%X, offset %u", hdr->xlp_info, fname, + LSN_FORMAT_ARGS(recptr), offset); return false; } @@ -1284,9 +1286,10 @@ XLogReaderValidatePageHeader(XLogReaderState *state, XLogRecPtr recptr, /* hmm, first page of file doesn't have a long header? */ report_invalid_record(state, - "invalid info bits %04X in WAL segment %s, offset %u", + "invalid info bits %04X in WAL segment %s, LSN %X/%X, offset %u", hdr->xlp_info, fname, + LSN_FORMAT_ARGS(recptr), offset); return false; } @@ -1303,9 +1306,10 @@ XLogReaderValidatePageHeader(XLogReaderState *state, XLogRecPtr recptr, XLogFileName(fname, state->seg.ws_tli, segno, state->segcxt.ws_segsize); report_invalid_record(state, - "unexpected pageaddr %X/%X in WAL segment %s, offset %u", + "unexpected pageaddr %X/%X in WAL segment %s, LSN %X/%X, offset %u", LSN_FORMAT_ARGS(hdr->xlp_pageaddr), fname, + LSN_FORMAT_ARGS(recptr), offset); return false; } @@ -1328,10 +1332,11 @@ XLogReaderValidatePageHeader(XLogReaderState *state, XLogRecPtr recptr, XLogFileName(fname, state->seg.ws_tli, segno, state->segcxt.ws_segsize); report_invalid_record(state, - "out-of-sequence timeline ID %u (after %u) in WAL segment %s, offset %u", + "out-of-sequence timeline ID %u (after %u) in WAL segment %s, LSN %X/%X, offset %u", hdr->xlp_tli, state->latestPageTLI, fname, + LSN_FORMAT_ARGS(recptr), offset); return false; } @@ -1556,6 +1561,7 @@ WALRead(XLogReaderState *state, errinfo->wre_req = segbytes; errinfo->wre_read = readbytes; errinfo->wre_off = startoff; + errinfo->wre_lsn = recptr; errinfo->wre_seg = state->seg; return false; } diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index cb07694aea..c6ffb29c05 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -3079,9 +3079,10 @@ ReadRecord(XLogPrefetcher *xlogprefetcher, int emode, XLogFileName(fname, xlogreader->seg.ws_tli, segno, wal_segment_size); ereport(emode_for_corrupt_record(emode, xlogreader->EndRecPtr), - (errmsg("unexpected timeline ID %u in WAL segment %s, offset %u", + (errmsg("unexpected timeline ID %u in WAL segment %s, LSN %X/%X, offset %u", xlogreader->latestPageTLI, fname, + LSN_FORMAT_ARGS(xlogreader->latestPagePtr), offset))); record = NULL; } @@ -3284,14 +3285,16 @@ retry: errno = save_errno; ereport(emode_for_corrupt_record(emode, targetPagePtr + reqLen), (errcode_for_file_access(), - errmsg("could not read from WAL segment %s, offset %u: %m", - fname, readOff))); + errmsg("could not read from WAL segment %s, LSN %X/%X, offset %u: %m", + fname, LSN_FORMAT_ARGS(targetPagePtr), + readOff))); } else ereport(emode_for_corrupt_record(emode, targetPagePtr + reqLen), (errcode(ERRCODE_DATA_CORRUPTED), - errmsg("could not read from WAL segment %s, offset %u: read %d of %zu", - fname, readOff, r, (Size) XLOG_BLCKSZ))); + errmsg("could not read from WAL segment %s, LSN %X/%X, offset %u: read %d of %zu", + fname, LSN_FORMAT_ARGS(targetPagePtr), + readOff, r, (Size) XLOG_BLCKSZ))); goto next_record_is_invalid; } pgstat_report_wait_end(); diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c index 563cba258d..9d171f2ad9 100644 --- a/src/backend/access/transam/xlogutils.c +++ b/src/backend/access/transam/xlogutils.c @@ -1051,15 +1051,17 @@ WALReadRaiseError(WALReadError *errinfo) errno = errinfo->wre_errno; ereport(ERROR, (errcode_for_file_access(), - errmsg("could not read from WAL segment %s, offset %d: %m", - fname, errinfo->wre_off))); + errmsg("could not read from WAL segment %s, LSN %X/%X, offset %d: %m", + fname, LSN_FORMAT_ARGS(errinfo->wre_lsn), + errinfo->wre_off))); } else if (errinfo->wre_read == 0) { ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), - errmsg("could not read from WAL segment %s, offset %d: read %d of %d", - fname, errinfo->wre_off, errinfo->wre_read, + errmsg("could not read from WAL segment %s, LSN %X/%X, offset %d: read %d of %d", + fname, LSN_FORMAT_ARGS(errinfo->wre_lsn), + errinfo->wre_off, errinfo->wre_read, errinfo->wre_req))); } } diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h index e87f91316a..1e47169b5a 100644 --- a/src/include/access/xlogreader.h +++ b/src/include/access/xlogreader.h @@ -386,6 +386,7 @@ typedef struct WALReadError int wre_off; /* Offset we tried to read from. */ int wre_req; /* Bytes requested to be read. */ int wre_read; /* Bytes read by the last read(). */ + XLogRecPtr wre_lsn; /* WAL LSN being read. */ WALOpenSegment wre_seg; /* Segment we tried to read from. */ } WALReadError; -- 2.34.1
From f8ed45d9fa59690d8b3375d658c1baedb8510195 Mon Sep 17 00:00:00 2001 From: Nathan Bossart <nathandbossart@gmail.com> Date: Mon, 19 Sep 2022 20:24:10 -0700 Subject: [PATCH v1 1/1] introduce pg_walfile_offset_lsn() --- doc/src/sgml/func.sgml | 14 +++++++ src/backend/access/transam/xlogfuncs.c | 39 ++++++++++++++++++++ src/include/catalog/pg_proc.dat | 5 +++ src/test/regress/expected/misc_functions.out | 19 ++++++++++ src/test/regress/sql/misc_functions.sql | 9 +++++ 5 files changed, 86 insertions(+) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 67eb380632..318ac22769 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -25891,6 +25891,20 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560 </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>pg_walfile_offset_lsn</primary> + </indexterm> + <function>pg_walfile_offset_lsn</function> ( <parameter>file_name</parameter> <type>text</type>, <parameter>file_offset</parameter> <type>integer</type> ) + <returnvalue>pg_lsn</returnvalue> + </para> + <para> + Converts a WAL file name and byte offset within that file to a + write-ahead log location. + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 27aeb6e281..75f58cb118 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -313,6 +313,45 @@ pg_last_wal_replay_lsn(PG_FUNCTION_ARGS) PG_RETURN_LSN(recptr); } +/* + * Compute an LSN given a WAL file name and decimal byte offset. + */ +Datum +pg_walfile_offset_lsn(PG_FUNCTION_ARGS) +{ + char *filename = text_to_cstring(PG_GETARG_TEXT_PP(0)); + int offset = PG_GETARG_INT32(1); + TimeLineID tli; + XLogSegNo segno; + XLogRecPtr result; + uint32 log; + uint32 seg; + + if (!IsXLogFileName(filename)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid WAL file name \"%s\"", filename))); + + sscanf(filename, "%08X%08X%08X", &tli, &log, &seg); + if (seg >= XLogSegmentsPerXLogId(wal_segment_size) || + (log == 0 && seg == 0) || + tli == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid WAL file name \"%s\"", filename))); + + if (offset < 0 || offset >= wal_segment_size) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"offset\" must not be negative or greater than or " + "equal to WAL segment size"))); + + XLogFromFileName(filename, &tli, &segno, wal_segment_size); + XLogSegNoOffsetToRecPtr(segno, offset, wal_segment_size, result); + + PG_RETURN_LSN(result); +} + /* * Compute an xlog file name and decimal byte offset given a WAL location, * such as is returned by pg_backup_stop() or pg_switch_wal(). diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index a07e737a33..224fe590ac 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6328,6 +6328,11 @@ proargtypes => 'pg_lsn', proallargtypes => '{pg_lsn,text,int4}', proargmodes => '{i,o,o}', proargnames => '{lsn,file_name,file_offset}', prosrc => 'pg_walfile_name_offset' }, +{ oid => '8205', + descr => 'wal location, given a wal filename and byte offset', + proname => 'pg_walfile_offset_lsn', prorettype => 'pg_lsn', + proargtypes => 'text int4', proargnames => '{file_name,file_offset}', + prosrc => 'pg_walfile_offset_lsn' }, { oid => '2851', descr => 'wal filename, given a wal location', proname => 'pg_walfile_name', prorettype => 'text', proargtypes => 'pg_lsn', prosrc => 'pg_walfile_name' }, diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out index 9f106c2a10..b2d6f23ac1 100644 --- a/src/test/regress/expected/misc_functions.out +++ b/src/test/regress/expected/misc_functions.out @@ -594,3 +594,22 @@ SELECT * FROM tenk1 a JOIN my_gen_series(1,10) g ON a.unique1 = g; Index Cond: (unique1 = g.g) (4 rows) +-- pg_walfile_offset_lsn +SELECT pg_walfile_offset_lsn('invalid', 15); +ERROR: invalid WAL file name "invalid" +SELECT pg_walfile_offset_lsn('0000000100000000FFFFFFFF', 15); +ERROR: invalid WAL file name "0000000100000000FFFFFFFF" +SELECT pg_walfile_offset_lsn('000000010000000000000000', 15); +ERROR: invalid WAL file name "000000010000000000000000" +SELECT pg_walfile_offset_lsn('000000000000000100000000', 15); +ERROR: invalid WAL file name "000000000000000100000000" +SELECT pg_walfile_offset_lsn('000000010000000100000000', -1); +ERROR: "offset" must not be negative or greater than or equal to WAL segment size +SELECT pg_walfile_offset_lsn('000000010000000100000000', 2000000000); +ERROR: "offset" must not be negative or greater than or equal to WAL segment size +SELECT pg_walfile_offset_lsn('000000010000000100000000', 15); + pg_walfile_offset_lsn +----------------------- + 1/F +(1 row) + diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql index 639e9b352c..cb54901029 100644 --- a/src/test/regress/sql/misc_functions.sql +++ b/src/test/regress/sql/misc_functions.sql @@ -223,3 +223,12 @@ SELECT * FROM tenk1 a JOIN my_gen_series(1,1000) g ON a.unique1 = g; EXPLAIN (COSTS OFF) SELECT * FROM tenk1 a JOIN my_gen_series(1,10) g ON a.unique1 = g; + +-- pg_walfile_offset_lsn +SELECT pg_walfile_offset_lsn('invalid', 15); +SELECT pg_walfile_offset_lsn('0000000100000000FFFFFFFF', 15); +SELECT pg_walfile_offset_lsn('000000010000000000000000', 15); +SELECT pg_walfile_offset_lsn('000000000000000100000000', 15); +SELECT pg_walfile_offset_lsn('000000010000000100000000', -1); +SELECT pg_walfile_offset_lsn('000000010000000100000000', 2000000000); +SELECT pg_walfile_offset_lsn('000000010000000100000000', 15); -- 2.25.1