Skip to content

Commit 2b3e467

Browse files
committed
Don't ERROR on PreallocXlogFiles() race condition.
Before a restartpoint finishes PreallocXlogFiles(), a startup process KeepFileRestoredFromArchive() call can unlink the preallocated segment. If a CHECKPOINT sql command had elicited the restartpoint experiencing the race condition, that sql command failed. Moreover, the restartpoint omitted its log_checkpoints message and some inessential resource reclamation. Prevent the ERROR by skipping open() of the segment. Since these consequences are so minor, no back-patch. Discussion: https://postgr.es/m/20210202151416.GB3304930@rfd.leadboat.com
1 parent 421484f commit 2b3e467

File tree

3 files changed

+58
-27
lines changed

3 files changed

+58
-27
lines changed

src/backend/access/transam/xlog.c

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,7 +2424,6 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
24242424
bool ispartialpage;
24252425
bool last_iteration;
24262426
bool finishing_seg;
2427-
bool added;
24282427
int curridx;
24292428
int npages;
24302429
int startidx;
@@ -2490,7 +2489,7 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
24902489
wal_segment_size);
24912490

24922491
/* create/use new log file */
2493-
openLogFile = XLogFileInit(openLogSegNo, &added);
2492+
openLogFile = XLogFileInit(openLogSegNo);
24942493
ReserveExternalFD();
24952494
}
24962495

@@ -3255,23 +3254,21 @@ XLogNeedsFlush(XLogRecPtr record)
32553254
}
32563255

32573256
/*
3258-
* Create a new XLOG file segment, or open a pre-existing one.
3257+
* Try to make a given XLOG file segment exist.
32593258
*
3260-
* logsegno: identify segment to be created/opened.
3259+
* logsegno: identify segment.
32613260
*
32623261
* *added: on return, true if this call raised the number of extant segments.
32633262
*
3264-
* Returns FD of opened file.
3263+
* path: on return, this char[MAXPGPATH] has the path to the logsegno file.
32653264
*
3266-
* Note: errors here are ERROR not PANIC because we might or might not be
3267-
* inside a critical section (eg, during checkpoint there is no reason to
3268-
* take down the system on failure). They will promote to PANIC if we are
3269-
* in a critical section.
3265+
* Returns -1 or FD of opened file. A -1 here is not an error; a caller
3266+
* wanting an open segment should attempt to open "path", which usually will
3267+
* succeed. (This is weird, but it's efficient for the callers.)
32703268
*/
3271-
int
3272-
XLogFileInit(XLogSegNo logsegno, bool *added)
3269+
static int
3270+
XLogFileInitInternal(XLogSegNo logsegno, bool *added, char *path)
32733271
{
3274-
char path[MAXPGPATH];
32753272
char tmppath[MAXPGPATH];
32763273
PGAlignedXLogBlock zbuffer;
32773274
XLogSegNo installed_segno;
@@ -3424,26 +3421,53 @@ XLogFileInit(XLogSegNo logsegno, bool *added)
34243421
*/
34253422
max_segno = logsegno + CheckPointSegments;
34263423
if (InstallXLogFileSegment(&installed_segno, tmppath, true, max_segno))
3424+
{
34273425
*added = true;
3426+
elog(DEBUG2, "done creating and filling new WAL file");
3427+
}
34283428
else
34293429
{
34303430
/*
34313431
* No need for any more future segments, or InstallXLogFileSegment()
3432-
* failed to rename the file into place. If the rename failed, opening
3433-
* the file below will fail.
3432+
* failed to rename the file into place. If the rename failed, a
3433+
* caller opening the file may fail.
34343434
*/
34353435
unlink(tmppath);
3436+
elog(DEBUG2, "abandoned new WAL file");
34363437
}
34373438

3439+
return -1;
3440+
}
3441+
3442+
/*
3443+
* Create a new XLOG file segment, or open a pre-existing one.
3444+
*
3445+
* logsegno: identify segment to be created/opened.
3446+
*
3447+
* Returns FD of opened file.
3448+
*
3449+
* Note: errors here are ERROR not PANIC because we might or might not be
3450+
* inside a critical section (eg, during checkpoint there is no reason to
3451+
* take down the system on failure). They will promote to PANIC if we are
3452+
* in a critical section.
3453+
*/
3454+
int
3455+
XLogFileInit(XLogSegNo logsegno)
3456+
{
3457+
bool ignore_added;
3458+
char path[MAXPGPATH];
3459+
int fd;
3460+
3461+
fd = XLogFileInitInternal(logsegno, &ignore_added, path);
3462+
if (fd >= 0)
3463+
return fd;
3464+
34383465
/* Now open original target segment (might not be file I just made) */
34393466
fd = BasicOpenFile(path, O_RDWR | PG_BINARY | get_sync_bit(sync_method));
34403467
if (fd < 0)
34413468
ereport(ERROR,
34423469
(errcode_for_file_access(),
34433470
errmsg("could not open file \"%s\": %m", path)));
3444-
3445-
elog(DEBUG2, "done creating and filling new WAL file");
3446-
34473471
return fd;
34483472
}
34493473

@@ -3903,22 +3927,33 @@ XLogFileClose(void)
39033927
* High-volume systems will be OK once they've built up a sufficient set of
39043928
* recycled log segments, but the startup transient is likely to include
39053929
* a lot of segment creations by foreground processes, which is not so good.
3930+
*
3931+
* XLogFileInitInternal() can ereport(ERROR). All known causes indicate big
3932+
* trouble; for example, a full filesystem is one cause. The checkpoint WAL
3933+
* and/or ControlFile updates already completed. If a RequestCheckpoint()
3934+
* initiated the present checkpoint and an ERROR ends this function, the
3935+
* command that called RequestCheckpoint() fails. That's not ideal, but it's
3936+
* not worth contorting more functions to use caller-specified elevel values.
3937+
* (With or without RequestCheckpoint(), an ERROR forestalls some inessential
3938+
* reporting and resource reclamation.)
39063939
*/
39073940
static void
39083941
PreallocXlogFiles(XLogRecPtr endptr)
39093942
{
39103943
XLogSegNo _logSegNo;
39113944
int lf;
39123945
bool added;
3946+
char path[MAXPGPATH];
39133947
uint64 offset;
39143948

39153949
XLByteToPrevSeg(endptr, _logSegNo, wal_segment_size);
39163950
offset = XLogSegmentOffset(endptr - 1, wal_segment_size);
39173951
if (offset >= (uint32) (0.75 * wal_segment_size))
39183952
{
39193953
_logSegNo++;
3920-
lf = XLogFileInit(_logSegNo, &added);
3921-
close(lf);
3954+
lf = XLogFileInitInternal(_logSegNo, &added, path);
3955+
if (lf >= 0)
3956+
close(lf);
39223957
if (added)
39233958
CheckpointStats.ckpt_segs_added++;
39243959
}
@@ -5214,7 +5249,6 @@ BootStrapXLOG(void)
52145249
XLogLongPageHeader longpage;
52155250
XLogRecord *record;
52165251
char *recptr;
5217-
bool added;
52185252
uint64 sysidentifier;
52195253
struct timeval tv;
52205254
pg_crc32c crc;
@@ -5311,7 +5345,7 @@ BootStrapXLOG(void)
53115345
record->xl_crc = crc;
53125346

53135347
/* Create first XLOG segment file */
5314-
openLogFile = XLogFileInit(1, &added);
5348+
openLogFile = XLogFileInit(1);
53155349

53165350
/*
53175351
* We needn't bother with Reserve/ReleaseExternalFD here, since we'll
@@ -5617,10 +5651,9 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
56175651
* The switch happened at a segment boundary, so just create the next
56185652
* segment on the new timeline.
56195653
*/
5620-
bool added;
56215654
int fd;
56225655

5623-
fd = XLogFileInit(startLogSegNo, &added);
5656+
fd = XLogFileInit(startLogSegNo);
56245657

56255658
if (close(fd) != 0)
56265659
{

src/backend/replication/walreceiver.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -885,8 +885,6 @@ XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr)
885885

886886
if (recvFile < 0 || !XLByteInSeg(recptr, recvSegNo, wal_segment_size))
887887
{
888-
bool added;
889-
890888
/*
891889
* fsync() and close current file before we switch to next one. We
892890
* would otherwise have to reopen this file to fsync it later
@@ -923,7 +921,7 @@ XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr)
923921

924922
/* Create/use new log file */
925923
XLByteToSeg(recptr, recvSegNo, wal_segment_size);
926-
recvFile = XLogFileInit(recvSegNo, &added);
924+
recvFile = XLogFileInit(recvSegNo);
927925
recvFileTLI = ThisTimeLineID;
928926
}
929927

src/include/access/xlog.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
296296
extern void XLogFlush(XLogRecPtr RecPtr);
297297
extern bool XLogBackgroundFlush(void);
298298
extern bool XLogNeedsFlush(XLogRecPtr RecPtr);
299-
extern int XLogFileInit(XLogSegNo segno, bool *added);
299+
extern int XLogFileInit(XLogSegNo segno);
300300
extern int XLogFileOpen(XLogSegNo segno);
301301

302302
extern void CheckXLogRemoved(XLogSegNo segno, TimeLineID tli);

0 commit comments

Comments
 (0)