Skip to content

Commit 3b02ea4

Browse files
committed
XLogReader general code cleanup
Some minor tweaks and comment additions, for cleanliness sake and to avoid having the upcoming timeline-following patch be polluted with unrelated cleanup. Extracted from a larger patch by Craig Ringer, reviewed by Andres Freund, with some additions by myself.
1 parent 50861cd commit 3b02ea4

File tree

5 files changed

+86
-30
lines changed

5 files changed

+86
-30
lines changed

src/backend/access/transam/xlogreader.c

+37-17
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
*
1111
* NOTES
1212
* See xlogreader.h for more notes on this facility.
13+
*
14+
* This file is compiled as both front-end and backend code, so it
15+
* may not use ereport, server-defined static variables, etc.
1316
*-------------------------------------------------------------------------
1417
*/
15-
1618
#include "postgres.h"
1719

1820
#include "access/transam.h"
@@ -192,14 +194,21 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
192194
{
193195
XLogRecord *record;
194196
XLogRecPtr targetPagePtr;
195-
bool randAccess = false;
197+
bool randAccess;
196198
uint32 len,
197199
total_len;
198200
uint32 targetRecOff;
199201
uint32 pageHeaderSize;
200202
bool gotheader;
201203
int readOff;
202204

205+
/*
206+
* randAccess indicates whether to verify the previous-record pointer of
207+
* the record we're reading. We only do this if we're reading
208+
* sequentially, which is what we initially assume.
209+
*/
210+
randAccess = false;
211+
203212
/* reset error state */
204213
*errormsg = NULL;
205214
state->errormsg_buf[0] = '\0';
@@ -208,6 +217,7 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
208217

209218
if (RecPtr == InvalidXLogRecPtr)
210219
{
220+
/* No explicit start point; read the record after the one we just read */
211221
RecPtr = state->EndRecPtr;
212222

213223
if (state->ReadRecPtr == InvalidXLogRecPtr)
@@ -223,11 +233,13 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
223233
else
224234
{
225235
/*
236+
* Caller supplied a position to start at.
237+
*
226238
* In this case, the passed-in record pointer should already be
227239
* pointing to a valid record starting position.
228240
*/
229241
Assert(XRecOffIsValid(RecPtr));
230-
randAccess = true; /* allow readPageTLI to go backwards too */
242+
randAccess = true;
231243
}
232244

233245
state->currRecPtr = RecPtr;
@@ -309,8 +321,10 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
309321
/* XXX: more validation should be done here */
310322
if (total_len < SizeOfXLogRecord)
311323
{
312-
report_invalid_record(state, "invalid record length at %X/%X",
313-
(uint32) (RecPtr >> 32), (uint32) RecPtr);
324+
report_invalid_record(state,
325+
"invalid record length at %X/%X: wanted %lu, got %u",
326+
(uint32) (RecPtr >> 32), (uint32) RecPtr,
327+
SizeOfXLogRecord, total_len);
314328
goto err;
315329
}
316330
gotheader = false;
@@ -463,12 +477,10 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
463477
err:
464478

465479
/*
466-
* Invalidate the xlog page we've cached. We might read from a different
467-
* source after failure.
480+
* Invalidate the read state. We might read from a different source after
481+
* failure.
468482
*/
469-
state->readSegNo = 0;
470-
state->readOff = 0;
471-
state->readLen = 0;
483+
XLogReaderInvalReadState(state);
472484

473485
if (state->errormsg_buf[0] != '\0')
474486
*errormsg = state->errormsg_buf;
@@ -572,18 +584,27 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
572584
if (!ValidXLogPageHeader(state, pageptr, hdr))
573585
goto err;
574586

575-
/* update cache information */
587+
/* update read state information */
576588
state->readSegNo = targetSegNo;
577589
state->readOff = targetPageOff;
578590
state->readLen = readLen;
579591

580592
return readLen;
581593

582594
err:
595+
XLogReaderInvalReadState(state);
596+
return -1;
597+
}
598+
599+
/*
600+
* Invalidate the xlogreader's read state to force a re-read.
601+
*/
602+
void
603+
XLogReaderInvalReadState(XLogReaderState *state)
604+
{
583605
state->readSegNo = 0;
584606
state->readOff = 0;
585607
state->readLen = 0;
586-
return -1;
587608
}
588609

589610
/*
@@ -600,8 +621,9 @@ ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
600621
if (record->xl_tot_len < SizeOfXLogRecord)
601622
{
602623
report_invalid_record(state,
603-
"invalid record length at %X/%X",
604-
(uint32) (RecPtr >> 32), (uint32) RecPtr);
624+
"invalid record length at %X/%X: wanted %lu, got %u",
625+
(uint32) (RecPtr >> 32), (uint32) RecPtr,
626+
SizeOfXLogRecord, record->xl_tot_len);
605627
return false;
606628
}
607629
if (record->xl_rmid > RM_MAX_ID)
@@ -907,11 +929,9 @@ XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
907929
err:
908930
out:
909931
/* Reset state to what we had before finding the record */
910-
state->readSegNo = 0;
911-
state->readOff = 0;
912-
state->readLen = 0;
913932
state->ReadRecPtr = saved_state.ReadRecPtr;
914933
state->EndRecPtr = saved_state.EndRecPtr;
934+
XLogReaderInvalReadState(state);
915935

916936
return found;
917937
}

src/backend/access/transam/xlogutils.c

+15-6
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@
1919

2020
#include <unistd.h>
2121

22-
#include "miscadmin.h"
23-
2422
#include "access/xlog.h"
2523
#include "access/xlog_internal.h"
2624
#include "access/xlogutils.h"
2725
#include "catalog/catalog.h"
26+
#include "miscadmin.h"
2827
#include "storage/smgr.h"
2928
#include "utils/guc.h"
3029
#include "utils/hsearch.h"
@@ -638,8 +637,17 @@ XLogTruncateRelation(RelFileNode rnode, ForkNumber forkNum,
638637
}
639638

640639
/*
641-
* TODO: This is duplicate code with pg_xlogdump, similar to walsender.c, but
642-
* we currently don't have the infrastructure (elog!) to share it.
640+
* Read 'count' bytes from WAL into 'buf', starting at location 'startptr'
641+
* in timeline 'tli'.
642+
*
643+
* Will open, and keep open, one WAL segment stored in the static file
644+
* descriptor 'sendFile'. This means if XLogRead is used once, there will
645+
* always be one descriptor left open until the process ends, but never
646+
* more than one.
647+
*
648+
* XXX This is very similar to pg_xlogdump's XLogDumpXLogRead and to XLogRead
649+
* in walsender.c but for small differences (such as lack of elog() in
650+
* frontend). Probably these should be merged at some point.
643651
*/
644652
static void
645653
XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
@@ -648,6 +656,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
648656
XLogRecPtr recptr;
649657
Size nbytes;
650658

659+
/* state maintained across calls */
651660
static int sendFile = -1;
652661
static XLogSegNo sendSegNo = 0;
653662
static uint32 sendOff = 0;
@@ -664,11 +673,11 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
664673

665674
startoff = recptr % XLogSegSize;
666675

676+
/* Do we need to switch to a different xlog segment? */
667677
if (sendFile < 0 || !XLByteInSeg(recptr, sendSegNo))
668678
{
669679
char path[MAXPGPATH];
670680

671-
/* Switch to another logfile segment */
672681
if (sendFile >= 0)
673682
close(sendFile);
674683

@@ -745,7 +754,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
745754
* Public because it would likely be very helpful for someone writing another
746755
* output method outside walsender, e.g. in a bgworker.
747756
*
748-
* TODO: The walsender has it's own version of this, but it relies on the
757+
* TODO: The walsender has its own version of this, but it relies on the
749758
* walsender's latch being set whenever WAL is flushed. No such infrastructure
750759
* exists for normal backends, so we have to do a check/sleep/repeat style of
751760
* loop for now.

src/backend/replication/logical/logicalfuncs.c

+17-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ logical_read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
115115
int reqLen, XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI)
116116
{
117117
return read_local_xlog_page(state, targetPagePtr, reqLen,
118-
targetRecPtr, cur_page, pageTLI);
118+
targetRecPtr, cur_page, pageTLI);
119119
}
120120

121121
/*
@@ -241,6 +241,10 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
241241

242242
PG_TRY();
243243
{
244+
/*
245+
* Passing InvalidXLogRecPtr here causes replay to start at the slot's
246+
* confirmed_flush.
247+
*/
244248
ctx = CreateDecodingContext(InvalidXLogRecPtr,
245249
options,
246250
logical_read_local_xlog_page,
@@ -263,6 +267,14 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
263267

264268
ctx->output_writer_private = p;
265269

270+
/*
271+
* We start reading xlog from the restart lsn, even though in
272+
* CreateDecodingContext we set the snapshot builder up using the
273+
* slot's confirmed_flush. This means we might read xlog we don't
274+
* actually decode rows from, but the snapshot builder might need it
275+
* to get to a consistent point. The point we start returning data to
276+
* *users* at is the candidate restart lsn from the decoding context.
277+
*/
266278
startptr = MyReplicationSlot->data.restart_lsn;
267279

268280
CurrentResourceOwner = ResourceOwnerCreate(CurrentResourceOwner, "logical decoding");
@@ -280,6 +292,10 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
280292
if (errm)
281293
elog(ERROR, "%s", errm);
282294

295+
/*
296+
* Now that we've set up the xlog reader state, subsequent calls
297+
* pass InvalidXLogRecPtr to say "continue from last record"
298+
*/
283299
startptr = InvalidXLogRecPtr;
284300

285301
/*

src/include/access/xlogreader.h

+13-4
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,22 @@ struct XLogReaderState
139139
* ----------------------------------------
140140
*/
141141

142-
/* Buffer for currently read page (XLOG_BLCKSZ bytes) */
142+
/*
143+
* Buffer for currently read page (XLOG_BLCKSZ bytes, valid up to at least
144+
* readLen bytes)
145+
*/
143146
char *readBuf;
147+
uint32 readLen;
144148

145-
/* last read segment, segment offset, read length, TLI */
149+
/* last read segment, segment offset, TLI for data currently in readBuf */
146150
XLogSegNo readSegNo;
147151
uint32 readOff;
148-
uint32 readLen;
149152
TimeLineID readPageTLI;
150153

151-
/* beginning of last page read, and its TLI */
154+
/*
155+
* beginning of prior page read, and its TLI. Doesn't necessarily
156+
* correspond to what's in readBuf; used for timeline sanity checks.
157+
*/
152158
XLogRecPtr latestPagePtr;
153159
TimeLineID latestPageTLI;
154160

@@ -174,6 +180,9 @@ extern void XLogReaderFree(XLogReaderState *state);
174180
extern struct XLogRecord *XLogReadRecord(XLogReaderState *state,
175181
XLogRecPtr recptr, char **errormsg);
176182

183+
/* Invalidate read state */
184+
extern void XLogReaderInvalReadState(XLogReaderState *state);
185+
177186
#ifdef FRONTEND
178187
extern XLogRecPtr XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr);
179188
#endif /* FRONTEND */

src/include/access/xlogutils.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
4747
extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
4848
extern void FreeFakeRelcacheEntry(Relation fakerel);
4949

50-
extern int read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
51-
int reqLen, XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI);
50+
extern int read_local_xlog_page(XLogReaderState *state,
51+
XLogRecPtr targetPagePtr, int reqLen,
52+
XLogRecPtr targetRecPtr, char *cur_page,
53+
TimeLineID *pageTLI);
5254

5355
#endif

0 commit comments

Comments
 (0)