Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d72792d

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 131fb7f commit d72792d

File tree

3 files changed

+209
-94
lines changed

3 files changed

+209
-94
lines changed

src/backend/access/transam/xlog.c

Lines changed: 189 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,8 @@ static int emode_for_corrupt_record(int emode, XLogRecPtr RecPtr);
782782
static void XLogFileClose(void);
783783
static void PreallocXlogFiles(XLogRecPtr endptr);
784784
static void RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr endptr);
785+
static void RemoveXlogFile(const char *segname, XLogRecPtr endptr);
786+
static void RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI);
785787
static void UpdateLastRemovedPtr(char *filename);
786788
static void ValidateXLOGDirectoryStructure(void);
787789
static void CleanupBackupHistory(void);
@@ -3743,32 +3745,17 @@ UpdateLastRemovedPtr(char *filename)
37433745
}
37443746

37453747
/*
3746-
* Recycle or remove all log files older or equal to passed segno
3748+
* Recycle or remove all log files older or equal to passed segno.
37473749
*
37483750
* endptr is current (or recent) end of xlog; this is used to determine
37493751
* whether we want to recycle rather than delete no-longer-wanted log files.
37503752
*/
37513753
static void
37523754
RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr endptr)
37533755
{
3754-
XLogSegNo endlogSegNo;
3755-
int max_advance;
37563756
DIR *xldir;
37573757
struct dirent *xlde;
37583758
char lastoff[MAXFNAMELEN];
3759-
char path[MAXPGPATH];
3760-
3761-
#ifdef WIN32
3762-
char newpath[MAXPGPATH];
3763-
#endif
3764-
struct stat statbuf;
3765-
3766-
/*
3767-
* Initialize info about where to try to recycle to. We allow recycling
3768-
* segments up to XLOGfileslop segments beyond the current XLOG location.
3769-
*/
3770-
XLByteToPrevSeg(endptr, endlogSegNo);
3771-
max_advance = XLOGfileslop;
37723759

37733760
xldir = AllocateDir(XLOGDIR);
37743761
if (xldir == NULL)
@@ -3789,6 +3776,11 @@ RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr endptr)
37893776

37903777
while ((xlde = ReadDir(xldir, XLOGDIR)) != NULL)
37913778
{
3779+
/* Ignore files that are not XLOG segments */
3780+
if (strlen(xlde->d_name) != 24 ||
3781+
strspn(xlde->d_name, "0123456789ABCDEF") != 24)
3782+
continue;
3783+
37923784
/*
37933785
* We ignore the timeline part of the XLOG segment identifiers in
37943786
* deciding whether a segment is still needed. This ensures that we
@@ -3800,92 +3792,110 @@ RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr endptr)
38003792
* We use the alphanumeric sorting property of the filenames to decide
38013793
* which ones are earlier than the lastoff segment.
38023794
*/
3803-
if (strlen(xlde->d_name) == 24 &&
3804-
strspn(xlde->d_name, "0123456789ABCDEF") == 24 &&
3805-
strcmp(xlde->d_name + 8, lastoff + 8) <= 0)
3795+
if (strcmp(xlde->d_name + 8, lastoff + 8) <= 0)
38063796
{
38073797
if (XLogArchiveCheckDone(xlde->d_name))
38083798
{
3809-
snprintf(path, MAXPGPATH, XLOGDIR "/%s", xlde->d_name);
3810-
38113799
/* Update the last removed location in shared memory first */
38123800
UpdateLastRemovedPtr(xlde->d_name);
38133801

3814-
/*
3815-
* Before deleting the file, see if it can be recycled as a
3816-
* future log segment. Only recycle normal files, pg_standby
3817-
* for example can create symbolic links pointing to a
3818-
* separate archive directory.
3819-
*/
3820-
if (lstat(path, &statbuf) == 0 && S_ISREG(statbuf.st_mode) &&
3821-
InstallXLogFileSegment(&endlogSegNo, path,
3822-
true, &max_advance, true))
3823-
{
3824-
ereport(DEBUG2,
3825-
(errmsg("recycled transaction log file \"%s\"",
3826-
xlde->d_name)));
3827-
CheckpointStats.ckpt_segs_recycled++;
3828-
/* Needn't recheck that slot on future iterations */
3829-
if (max_advance > 0)
3830-
{
3831-
endlogSegNo++;
3832-
max_advance--;
3833-
}
3834-
}
3835-
else
3836-
{
3837-
/* No need for any more future segments... */
3838-
int rc;
3802+
RemoveXlogFile(xlde->d_name, endptr);
3803+
}
3804+
}
3805+
}
38393806

3840-
ereport(DEBUG2,
3841-
(errmsg("removing transaction log file \"%s\"",
3842-
xlde->d_name)));
3807+
FreeDir(xldir);
3808+
}
38433809

3810+
/*
3811+
* Recycle or remove a log file that's no longer needed.
3812+
*
3813+
* endptr is current (or recent) end of xlog; this is used to determine
3814+
* whether we want to recycle rather than delete no-longer-wanted log files.
3815+
*/
3816+
static void
3817+
RemoveXlogFile(const char *segname, XLogRecPtr endptr)
3818+
{
3819+
char path[MAXPGPATH];
38443820
#ifdef WIN32
3821+
char newpath[MAXPGPATH];
3822+
#endif
3823+
struct stat statbuf;
3824+
XLogSegNo endlogSegNo;
3825+
int max_advance;
38453826

3846-
/*
3847-
* On Windows, if another process (e.g another backend)
3848-
* holds the file open in FILE_SHARE_DELETE mode, unlink
3849-
* will succeed, but the file will still show up in
3850-
* directory listing until the last handle is closed. To
3851-
* avoid confusing the lingering deleted file for a live
3852-
* WAL file that needs to be archived, rename it before
3853-
* deleting it.
3854-
*
3855-
* If another process holds the file open without
3856-
* FILE_SHARE_DELETE flag, rename will fail. We'll try
3857-
* again at the next checkpoint.
3858-
*/
3859-
snprintf(newpath, MAXPGPATH, "%s.deleted", path);
3860-
if (rename(path, newpath) != 0)
3861-
{
3862-
ereport(LOG,
3863-
(errcode_for_file_access(),
3864-
errmsg("could not rename old transaction log file \"%s\": %m",
3865-
path)));
3866-
continue;
3867-
}
3868-
rc = unlink(newpath);
3827+
/*
3828+
* Initialize info about where to try to recycle to. We allow recycling
3829+
* segments up to XLOGfileslop segments beyond the current XLOG location.
3830+
*/
3831+
XLByteToPrevSeg(endptr, endlogSegNo);
3832+
max_advance = XLOGfileslop;
3833+
3834+
snprintf(path, MAXPGPATH, XLOGDIR "/%s", segname);
3835+
3836+
/*
3837+
* Before deleting the file, see if it can be recycled as a future log
3838+
* segment. Only recycle normal files, pg_standby for example can create
3839+
* symbolic links pointing to a separate archive directory.
3840+
*/
3841+
if (lstat(path, &statbuf) == 0 && S_ISREG(statbuf.st_mode) &&
3842+
InstallXLogFileSegment(&endlogSegNo, path,
3843+
true, &max_advance, true))
3844+
{
3845+
ereport(DEBUG2,
3846+
(errmsg("recycled transaction log file \"%s\"", segname)));
3847+
CheckpointStats.ckpt_segs_recycled++;
3848+
/* Needn't recheck that slot on future iterations */
3849+
if (max_advance > 0)
3850+
{
3851+
endlogSegNo++;
3852+
max_advance--;
3853+
}
3854+
}
3855+
else
3856+
{
3857+
/* No need for any more future segments... */
3858+
int rc;
3859+
3860+
ereport(DEBUG2,
3861+
(errmsg("removing transaction log file \"%s\"", segname)));
3862+
3863+
#ifdef WIN32
3864+
/*
3865+
* On Windows, if another process (e.g another backend) holds the file
3866+
* open in FILE_SHARE_DELETE mode, unlink will succeed, but the file
3867+
* will still show up in directory listing until the last handle is
3868+
* closed. To avoid confusing the lingering deleted file for a live
3869+
* WAL file that needs to be archived, rename it before deleting it.
3870+
*
3871+
* If another process holds the file open without FILE_SHARE_DELETE
3872+
* flag, rename will fail. We'll try again at the next checkpoint.
3873+
*/
3874+
snprintf(newpath, MAXPGPATH, "%s.deleted", path);
3875+
if (rename(path, newpath) != 0)
3876+
{
3877+
ereport(LOG,
3878+
(errcode_for_file_access(),
3879+
errmsg("could not rename old transaction log file \"%s\": %m",
3880+
path)));
3881+
return;
3882+
}
3883+
rc = unlink(newpath);
38693884
#else
3870-
rc = unlink(path);
3885+
rc = unlink(path);
38713886
#endif
3872-
if (rc != 0)
3873-
{
3874-
ereport(LOG,
3875-
(errcode_for_file_access(),
3876-
errmsg("could not remove old transaction log file \"%s\": %m",
3877-
path)));
3878-
continue;
3879-
}
3880-
CheckpointStats.ckpt_segs_removed++;
3881-
}
3882-
3883-
XLogArchiveCleanup(xlde->d_name);
3884-
}
3887+
if (rc != 0)
3888+
{
3889+
ereport(LOG,
3890+
(errcode_for_file_access(),
3891+
errmsg("could not remove old transaction log file \"%s\": %m",
3892+
path)));
3893+
return;
38853894
}
3895+
CheckpointStats.ckpt_segs_removed++;
38863896
}
38873897

3888-
FreeDir(xldir);
3898+
XLogArchiveCleanup(segname);
38893899
}
38903900

38913901
/*
@@ -5407,6 +5417,76 @@ exitArchiveRecovery(TimeLineID endTLI, XLogSegNo endLogSegNo)
54075417
(errmsg("archive recovery complete")));
54085418
}
54095419

5420+
/*
5421+
* Remove WAL files that are not part of the given timeline's history.
5422+
*
5423+
* This is called during recovery, whenever we switch to follow a new
5424+
* timeline, and at the end of recovery when we create a new timeline. We
5425+
* wouldn't otherwise care about extra WAL files lying in pg_xlog, but they
5426+
* can be pre-allocated or recycled WAL segments on the old timeline that we
5427+
* haven't used yet, and contain garbage. If we just leave them in pg_xlog,
5428+
* they will eventually be archived, and we can't let that happen. Files that
5429+
* belong to our timeline history are valid, because we have successfully
5430+
* replayed them, but from others we can't be sure.
5431+
*
5432+
* 'switchpoint' is the current point in WAL where we switch to new timeline,
5433+
* and 'newTLI' is the new timeline we switch to.
5434+
*/
5435+
static void
5436+
RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI)
5437+
{
5438+
DIR *xldir;
5439+
struct dirent *xlde;
5440+
char switchseg[MAXFNAMELEN];
5441+
XLogSegNo endLogSegNo;
5442+
5443+
XLByteToPrevSeg(switchpoint, endLogSegNo);
5444+
5445+
xldir = AllocateDir(XLOGDIR);
5446+
if (xldir == NULL)
5447+
ereport(ERROR,
5448+
(errcode_for_file_access(),
5449+
errmsg("could not open transaction log directory \"%s\": %m",
5450+
XLOGDIR)));
5451+
5452+
/*
5453+
* Construct a filename of the last segment to be kept.
5454+
*/
5455+
XLogFileName(switchseg, newTLI, endLogSegNo);
5456+
5457+
elog(DEBUG2, "attempting to remove WAL segments newer than log file %s",
5458+
switchseg);
5459+
5460+
while ((xlde = ReadDir(xldir, XLOGDIR)) != NULL)
5461+
{
5462+
/* Ignore files that are not XLOG segments */
5463+
if (strlen(xlde->d_name) != 24 ||
5464+
strspn(xlde->d_name, "0123456789ABCDEF") != 24)
5465+
continue;
5466+
5467+
/*
5468+
* Remove files that are on a timeline older than the new one we're
5469+
* switching to, but with a segment number >= the first segment on
5470+
* the new timeline.
5471+
*/
5472+
if (strncmp(xlde->d_name, switchseg, 8) < 0 &&
5473+
strcmp(xlde->d_name + 8, switchseg + 8) > 0)
5474+
{
5475+
/*
5476+
* If the file has already been marked as .ready, however, don't
5477+
* remove it yet. It should be OK to remove it - files that are
5478+
* not part of our timeline history are not required for recovery
5479+
* - but seems safer to let them be archived and removed later.
5480+
*/
5481+
if (!XLogArchiveIsReady(xlde->d_name))
5482+
RemoveXlogFile(xlde->d_name, switchpoint);
5483+
}
5484+
}
5485+
5486+
FreeDir(xldir);
5487+
}
5488+
5489+
54105490
/*
54115491
* Extract timestamp from WAL record.
54125492
*
@@ -6775,9 +6855,9 @@ StartupXLOG(void)
67756855
*/
67766856
if (record->xl_rmid == RM_XLOG_ID)
67776857
{
6858+
uint8 info = record->xl_info & ~XLR_INFO_MASK;
67786859
TimeLineID newTLI = ThisTimeLineID;
67796860
TimeLineID prevTLI = ThisTimeLineID;
6780-
uint8 info = record->xl_info & ~XLR_INFO_MASK;
67816861

67826862
if (info == XLOG_CHECKPOINT_SHUTDOWN)
67836863
{
@@ -6845,12 +6925,21 @@ StartupXLOG(void)
68456925
/* Allow read-only connections if we're consistent now */
68466926
CheckRecoveryConsistency();
68476927

6848-
/*
6849-
* If this record was a timeline switch, wake up any
6850-
* walsenders to notice that we are on a new timeline.
6851-
*/
6852-
if (switchedTLI && AllowCascadeReplication())
6853-
WalSndWakeup();
6928+
if (switchedTLI)
6929+
{
6930+
/*
6931+
* Before we go further on the new timeline, clean up any
6932+
* (possibly bogus) future WAL segments on the old one.
6933+
*/
6934+
RemoveNonParentXlogFiles(EndRecPtr, ThisTimeLineID);
6935+
6936+
/*
6937+
* Wake up any walsenders to notice that we are on a new
6938+
* timeline.
6939+
*/
6940+
if (AllowCascadeReplication())
6941+
WalSndWakeup();
6942+
}
68546943

68556944
/* Exit loop if we reached inclusive recovery target */
68566945
if (recoveryStopsAfter(record))
@@ -7177,6 +7266,12 @@ StartupXLOG(void)
71777266
true);
71787267
}
71797268

7269+
/*
7270+
* Clean up any (possibly bogus) future WAL segments on the old timeline.
7271+
*/
7272+
if (ArchiveRecoveryRequested)
7273+
RemoveNonParentXlogFiles(EndOfLog, ThisTimeLineID);
7274+
71807275
/*
71817276
* Preallocate additional log files, if wanted.
71827277
*/

src/backend/access/transam/xlogarchive.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,25 @@ XLogArchiveIsBusy(const char *xlog)
693693
return true;
694694
}
695695

696+
/*
697+
* XLogArchiveIsReady
698+
*
699+
* Check to see if an XLOG segment file has an archive notification (.ready)
700+
* file.
701+
*/
702+
bool
703+
XLogArchiveIsReady(const char *xlog)
704+
{
705+
char archiveStatusPath[MAXPGPATH];
706+
struct stat stat_buf;
707+
708+
StatusFilePath(archiveStatusPath, xlog, ".ready");
709+
if (stat(archiveStatusPath, &stat_buf) == 0)
710+
return true;
711+
712+
return false;
713+
}
714+
696715
/*
697716
* XLogArchiveCleanup
698717
*

src/include/access/xlog_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ extern void XLogArchiveNotifySeg(XLogSegNo segno);
283283
extern void XLogArchiveForceDone(const char *xlog);
284284
extern bool XLogArchiveCheckDone(const char *xlog);
285285
extern bool XLogArchiveIsBusy(const char *xlog);
286+
extern bool XLogArchiveIsReady(const char *xlog);
286287
extern void XLogArchiveCleanup(const char *xlog);
287288

288289
#endif /* XLOG_INTERNAL_H */

0 commit comments

Comments
 (0)