Skip to content

Commit 5b69381

Browse files
committed
Don't archive bogus recycled or preallocated files after timeline switch.
After a timeline switch, we would leave behind recycled WAL segments that are in the future, but on the old timeline. After promotion, and after they become old enough to be recycled again, we would notice that they don't have a .ready or .done file, create a .ready file for them, and archive them. That's bogus, because the files contain garbage, recycled from an older timeline (or prealloced as zeros). We shouldn't archive such files. This could happen when we're following a timeline switch during replay, or when we switch to new timeline at end-of-recovery. To fix, whenever we switch to a new timeline, scan the data directory for WAL segments on the old timeline, but with a higher segment number, and remove them. Those don't belong to our timeline history, and are most likely bogus recycled or preallocated files. They could also be valid files that we streamed from the primary ahead of time, but in any case, they're not needed to recover to the new timeline.
1 parent 595bc97 commit 5b69381

File tree

1 file changed

+197
-87
lines changed
  • src/backend/access/transam

1 file changed

+197
-87
lines changed

src/backend/access/transam/xlog.c

Lines changed: 197 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ static void XLogArchiveNotify(const char *xlog);
564564
static void XLogArchiveNotifySeg(uint32 log, uint32 seg);
565565
static bool XLogArchiveCheckDone(const char *xlog);
566566
static bool XLogArchiveIsBusy(const char *xlog);
567+
extern bool XLogArchiveIsReady(const char *xlog);
567568
static void XLogArchiveCleanup(const char *xlog);
568569
static void readRecoveryCommandFile(void);
569570
static void exitArchiveRecovery(TimeLineID endTLI,
@@ -598,6 +599,8 @@ static void ExecuteRecoveryCommand(char *command, char *commandName,
598599
bool failOnerror);
599600
static void PreallocXlogFiles(XLogRecPtr endptr);
600601
static void RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr);
602+
static void RemoveXlogFile(const char *segname, XLogRecPtr endptr);
603+
static void RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI);
601604
static void UpdateLastRemovedPtr(char *filename);
602605
static void ValidateXLOGDirectoryStructure(void);
603606
static void CleanupBackupHistory(void);
@@ -1405,6 +1408,25 @@ XLogArchiveIsBusy(const char *xlog)
14051408
return true;
14061409
}
14071410

1411+
/*
1412+
* XLogArchiveIsReady
1413+
*
1414+
* Check to see if an XLOG segment file has an archive notification (.ready)
1415+
* file.
1416+
*/
1417+
bool
1418+
XLogArchiveIsReady(const char *xlog)
1419+
{
1420+
char archiveStatusPath[MAXPGPATH];
1421+
struct stat stat_buf;
1422+
1423+
StatusFilePath(archiveStatusPath, xlog, ".ready");
1424+
if (stat(archiveStatusPath, &stat_buf) == 0)
1425+
return true;
1426+
1427+
return false;
1428+
}
1429+
14081430
/*
14091431
* XLogArchiveCleanup
14101432
*
@@ -3274,25 +3296,9 @@ UpdateLastRemovedPtr(char *filename)
32743296
static void
32753297
RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr)
32763298
{
3277-
uint32 endlogId;
3278-
uint32 endlogSeg;
3279-
int max_advance;
32803299
DIR *xldir;
32813300
struct dirent *xlde;
32823301
char lastoff[MAXFNAMELEN];
3283-
char path[MAXPGPATH];
3284-
3285-
#ifdef WIN32
3286-
char newpath[MAXPGPATH];
3287-
#endif
3288-
struct stat statbuf;
3289-
3290-
/*
3291-
* Initialize info about where to try to recycle to. We allow recycling
3292-
* segments up to XLOGfileslop segments beyond the current XLOG location.
3293-
*/
3294-
XLByteToPrevSeg(endptr, endlogId, endlogSeg);
3295-
max_advance = XLOGfileslop;
32963302

32973303
xldir = AllocateDir(XLOGDIR);
32983304
if (xldir == NULL)
@@ -3308,6 +3314,11 @@ RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr)
33083314

33093315
while ((xlde = ReadDir(xldir, XLOGDIR)) != NULL)
33103316
{
3317+
/* Ignore files that are not XLOG segments */
3318+
if (strlen(xlde->d_name) != 24 ||
3319+
strspn(xlde->d_name, "0123456789ABCDEF") != 24)
3320+
continue;
3321+
33113322
/*
33123323
* We ignore the timeline part of the XLOG segment identifiers in
33133324
* deciding whether a segment is still needed. This ensures that we
@@ -3319,9 +3330,7 @@ RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr)
33193330
* We use the alphanumeric sorting property of the filenames to decide
33203331
* which ones are earlier than the lastoff segment.
33213332
*/
3322-
if (strlen(xlde->d_name) == 24 &&
3323-
strspn(xlde->d_name, "0123456789ABCDEF") == 24 &&
3324-
strcmp(xlde->d_name + 8, lastoff + 8) <= 0)
3333+
if (strcmp(xlde->d_name + 8, lastoff + 8) <= 0)
33253334
{
33263335
/*
33273336
* Normally we don't delete old XLOG files during recovery to
@@ -3336,86 +3345,107 @@ RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr)
33363345
*/
33373346
if (WalRcvInProgress() || XLogArchiveCheckDone(xlde->d_name))
33383347
{
3339-
snprintf(path, MAXPGPATH, XLOGDIR "/%s", xlde->d_name);
3340-
33413348
/* Update the last removed location in shared memory first */
33423349
UpdateLastRemovedPtr(xlde->d_name);
33433350

3344-
/*
3345-
* Before deleting the file, see if it can be recycled as a
3346-
* future log segment. Only recycle normal files, pg_standby
3347-
* for example can create symbolic links pointing to a
3348-
* separate archive directory.
3349-
*/
3350-
if (lstat(path, &statbuf) == 0 && S_ISREG(statbuf.st_mode) &&
3351-
InstallXLogFileSegment(&endlogId, &endlogSeg, path,
3352-
true, &max_advance, true))
3353-
{
3354-
ereport(DEBUG2,
3355-
(errmsg("recycled transaction log file \"%s\"",
3356-
xlde->d_name)));
3357-
CheckpointStats.ckpt_segs_recycled++;
3358-
/* Needn't recheck that slot on future iterations */
3359-
if (max_advance > 0)
3360-
{
3361-
NextLogSeg(endlogId, endlogSeg);
3362-
max_advance--;
3363-
}
3364-
}
3365-
else
3366-
{
3367-
/* No need for any more future segments... */
3368-
int rc;
3351+
RemoveXlogFile(xlde->d_name, endptr);
3352+
}
3353+
}
3354+
}
33693355

3370-
ereport(DEBUG2,
3371-
(errmsg("removing transaction log file \"%s\"",
3372-
xlde->d_name)));
3356+
FreeDir(xldir);
3357+
}
33733358

3359+
/*
3360+
* Recycle or remove a log file that's no longer needed.
3361+
*
3362+
* endptr is current (or recent) end of xlog; this is used to determine
3363+
* whether we want to recycle rather than delete no-longer-wanted log files.
3364+
*/
3365+
static void
3366+
RemoveXlogFile(const char *segname, XLogRecPtr endptr)
3367+
{
3368+
char path[MAXPGPATH];
33743369
#ifdef WIN32
3370+
char newpath[MAXPGPATH];
3371+
#endif
3372+
struct stat statbuf;
3373+
uint32 endlogId;
3374+
uint32 endlogSeg;
3375+
int max_advance;
33753376

3376-
/*
3377-
* On Windows, if another process (e.g another backend)
3378-
* holds the file open in FILE_SHARE_DELETE mode, unlink
3379-
* will succeed, but the file will still show up in
3380-
* directory listing until the last handle is closed. To
3381-
* avoid confusing the lingering deleted file for a live
3382-
* WAL file that needs to be archived, rename it before
3383-
* deleting it.
3384-
*
3385-
* If another process holds the file open without
3386-
* FILE_SHARE_DELETE flag, rename will fail. We'll try
3387-
* again at the next checkpoint.
3388-
*/
3389-
snprintf(newpath, MAXPGPATH, "%s.deleted", path);
3390-
if (rename(path, newpath) != 0)
3391-
{
3392-
ereport(LOG,
3393-
(errcode_for_file_access(),
3394-
errmsg("could not rename old transaction log file \"%s\": %m",
3395-
path)));
3396-
continue;
3397-
}
3398-
rc = unlink(newpath);
3377+
/*
3378+
* Initialize info about where to try to recycle to. We allow recycling
3379+
* segments up to XLOGfileslop segments beyond the current XLOG location.
3380+
*/
3381+
XLByteToPrevSeg(endptr, endlogId, endlogSeg);
3382+
max_advance = XLOGfileslop;
3383+
3384+
snprintf(path, MAXPGPATH, XLOGDIR "/%s", segname);
3385+
3386+
/*
3387+
* Before deleting the file, see if it can be recycled as a future log
3388+
* segment. Only recycle normal files, pg_standby for example can create
3389+
* symbolic links pointing to a separate archive directory.
3390+
*/
3391+
if (lstat(path, &statbuf) == 0 && S_ISREG(statbuf.st_mode) &&
3392+
InstallXLogFileSegment(&endlogId, &endlogSeg, path,
3393+
true, &max_advance, true))
3394+
{
3395+
ereport(DEBUG2,
3396+
(errmsg("recycled transaction log file \"%s\"", segname)));
3397+
CheckpointStats.ckpt_segs_recycled++;
3398+
/* Needn't recheck that slot on future iterations */
3399+
if (max_advance > 0)
3400+
{
3401+
NextLogSeg(endlogId, endlogSeg);
3402+
max_advance--;
3403+
}
3404+
}
3405+
else
3406+
{
3407+
/* No need for any more future segments... */
3408+
int rc;
3409+
3410+
ereport(DEBUG2,
3411+
(errmsg("removing transaction log file \"%s\"", segname)));
3412+
3413+
#ifdef WIN32
3414+
/*
3415+
* On Windows, if another process (e.g another backend) holds the file
3416+
* open in FILE_SHARE_DELETE mode, unlink will succeed, but the file
3417+
* will still show up in directory listing until the last handle is
3418+
* closed. To avoid confusing the lingering deleted file for a live
3419+
* WAL file that needs to be archived, rename it before deleting it.
3420+
*
3421+
* If another process holds the file open without FILE_SHARE_DELETE
3422+
* flag, rename will fail. We'll try again at the next checkpoint.
3423+
*/
3424+
snprintf(newpath, MAXPGPATH, "%s.deleted", path);
3425+
if (rename(path, newpath) != 0)
3426+
{
3427+
ereport(LOG,
3428+
(errcode_for_file_access(),
3429+
errmsg("could not rename old transaction log file \"%s\": %m",
3430+
path)));
3431+
return;
3432+
}
3433+
rc = unlink(newpath);
33993434
#else
3400-
rc = unlink(path);
3435+
rc = unlink(path);
34013436
#endif
3402-
if (rc != 0)
3403-
{
3404-
ereport(LOG,
3405-
(errcode_for_file_access(),
3406-
errmsg("could not remove old transaction log file \"%s\": %m",
3407-
path)));
3408-
continue;
3409-
}
3410-
CheckpointStats.ckpt_segs_removed++;
3411-
}
3412-
3413-
XLogArchiveCleanup(xlde->d_name);
3414-
}
3437+
if (rc != 0)
3438+
{
3439+
ereport(LOG,
3440+
(errcode_for_file_access(),
3441+
errmsg("could not remove old transaction log file \"%s\": %m",
3442+
path)));
3443+
return;
34153444
}
3445+
CheckpointStats.ckpt_segs_removed++;
34163446
}
34173447

3418-
FreeDir(xldir);
3448+
XLogArchiveCleanup(segname);
34193449
}
34203450

34213451
/*
@@ -5501,6 +5531,76 @@ exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg)
55015531
(errmsg("archive recovery complete")));
55025532
}
55035533

5534+
/*
5535+
* Remove WAL files that are not part of the given timeline's history.
5536+
*
5537+
* This is called during recovery, whenever we switch to follow a new
5538+
* timeline, and at the end of recovery when we create a new timeline. We
5539+
* wouldn't otherwise care about extra WAL files lying in pg_xlog, but they
5540+
* can be pre-allocated or recycled WAL segments on the old timeline that we
5541+
* haven't used yet, and contain garbage. If we just leave them in pg_xlog,
5542+
* they will eventually be archived, and we can't let that happen. Files that
5543+
* belong to our timeline history are valid, because we have successfully
5544+
* replayed them, but from others we can't be sure.
5545+
*
5546+
* 'switchpoint' is the current point in WAL where we switch to new timeline,
5547+
* and 'newTLI' is the new timeline we switch to.
5548+
*/
5549+
static void
5550+
RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI)
5551+
{
5552+
DIR *xldir;
5553+
struct dirent *xlde;
5554+
char switchseg[MAXFNAMELEN];
5555+
uint32 endlogId;
5556+
uint32 endlogSeg;
5557+
5558+
XLByteToPrevSeg(switchpoint, endlogId, endlogSeg);
5559+
5560+
xldir = AllocateDir(XLOGDIR);
5561+
if (xldir == NULL)
5562+
ereport(ERROR,
5563+
(errcode_for_file_access(),
5564+
errmsg("could not open transaction log directory \"%s\": %m",
5565+
XLOGDIR)));
5566+
5567+
/*
5568+
* Construct a filename of the last segment to be kept.
5569+
*/
5570+
XLogFileName(switchseg, newTLI, endlogId, endlogSeg);
5571+
5572+
elog(DEBUG2, "attempting to remove WAL segments newer than log file %s",
5573+
switchseg);
5574+
5575+
while ((xlde = ReadDir(xldir, XLOGDIR)) != NULL)
5576+
{
5577+
/* Ignore files that are not XLOG segments */
5578+
if (strlen(xlde->d_name) != 24 ||
5579+
strspn(xlde->d_name, "0123456789ABCDEF") != 24)
5580+
continue;
5581+
5582+
/*
5583+
* Remove files that are on a timeline older than the new one we're
5584+
* switching to, but with a segment number >= the first segment on
5585+
* the new timeline.
5586+
*/
5587+
if (strncmp(xlde->d_name, switchseg, 8) < 0 &&
5588+
strcmp(xlde->d_name + 8, switchseg + 8) > 0)
5589+
{
5590+
/*
5591+
* If the file has already been marked as .ready, however, don't
5592+
* remove it yet. It should be OK to remove it - files that are
5593+
* not part of our timeline history are not required for recovery
5594+
* - but seems safer to let them be archived and removed later.
5595+
*/
5596+
if (!XLogArchiveIsReady(xlde->d_name))
5597+
RemoveXlogFile(xlde->d_name, switchpoint);
5598+
}
5599+
}
5600+
5601+
FreeDir(xldir);
5602+
}
5603+
55045604
/*
55055605
* For point-in-time recovery, this function decides whether we want to
55065606
* stop applying the XLOG at or after the current record.
@@ -5771,6 +5871,7 @@ StartupXLOG(void)
57715871
bool wasShutdown;
57725872
bool reachedStopPoint = false;
57735873
bool haveBackupLabel = false;
5874+
bool didArchiveRecovery = false;
57745875
XLogRecPtr RecPtr,
57755876
checkPointLoc,
57765877
EndOfLog;
@@ -6469,7 +6570,10 @@ StartupXLOG(void)
64696570
* we will use that below.)
64706571
*/
64716572
if (InArchiveRecovery)
6573+
{
6574+
didArchiveRecovery = true;
64726575
exitArchiveRecovery(curFileTLI, endLogId, endLogSeg);
6576+
}
64736577

64746578
/*
64756579
* Prepare to write WAL starting at EndOfLog position, and init xlog
@@ -6582,6 +6686,12 @@ StartupXLOG(void)
65826686
true);
65836687
}
65846688

6689+
/*
6690+
* Clean up any (possibly bogus) future WAL segments on the old timeline.
6691+
*/
6692+
if (didArchiveRecovery)
6693+
RemoveNonParentXlogFiles(EndOfLog, ThisTimeLineID);
6694+
65856695
/*
65866696
* Preallocate additional log files, if wanted.
65876697
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy