Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitad12311

Browse files
committed
Avoid creating archive status ".ready" files too early
WAL records may span multiple segments, but XLogWrite() does notwait for the entire record to be written out to disk beforecreating archive status files. Instead, as soon as the last WAL page ofthe segment is written, the archive status file is created, and thearchiver may process it. If PostgreSQL crashes before it is able towrite and flush the rest of the record (in the next WAL segment), thewrong version of the first segment file lingers in the archive, whichcauses operations such as point-in-time restores to fail.To fix this, keep track of records that span across segments and ensurethat segments are only marked ready-for-archival once such records havebeen completely written to disk.This has always been wrong, so backpatch all the way back.Author: Nathan Bossart <bossartn@amazon.com>Reviewed-by: Kyotaro Horiguchi <horikyota.ntt@gmail.com>Reviewed-by: Ryo Matsumura <matsumura.ryo@fujitsu.com>Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>Discussion:https://postgr.es/m/CBDDFA01-6E40-46BB-9F98-9340F4379505@amazon.com
1 parent29f9423 commitad12311

File tree

4 files changed

+215
-10
lines changed

4 files changed

+215
-10
lines changed

‎src/backend/access/transam/xlog.c

Lines changed: 206 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,18 @@ typedef struct XLogCtlData
725725
XLogRecPtrlastFpwDisableRecPtr;
726726

727727
slock_tinfo_lck;/* locks shared variables shown above */
728+
729+
/*
730+
* Variables used to track segment-boundary-crossing WAL records. See
731+
* RegisterSegmentBoundary. Protected by segtrack_lck.
732+
*/
733+
XLogSegNolastNotifiedSeg;
734+
XLogSegNoearliestSegBoundary;
735+
XLogRecPtrearliestSegBoundaryEndPtr;
736+
XLogSegNolatestSegBoundary;
737+
XLogRecPtrlatestSegBoundaryEndPtr;
738+
739+
slock_tsegtrack_lck;/* locks shared variables shown above */
728740
}XLogCtlData;
729741

730742
staticXLogCtlData*XLogCtl=NULL;
@@ -921,6 +933,7 @@ static void RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr lastredoptr, XLogRecP
921933
staticvoidRemoveXlogFile(constchar*segname,XLogRecPtrlastredoptr,XLogRecPtrendptr);
922934
staticvoidUpdateLastRemovedPtr(char*filename);
923935
staticvoidValidateXLOGDirectoryStructure(void);
936+
staticvoidRegisterSegmentBoundary(XLogSegNoseg,XLogRecPtrpos);
924937
staticvoidCleanupBackupHistory(void);
925938
staticvoidUpdateMinRecoveryPoint(XLogRecPtrlsn,boolforce);
926939
staticXLogRecord*ReadRecord(XLogReaderState*xlogreader,
@@ -1154,23 +1167,56 @@ XLogInsertRecord(XLogRecData *rdata,
11541167
END_CRIT_SECTION();
11551168

11561169
/*
1157-
* Update shared LogwrtRqst.Write, if we crossed page boundary.
1170+
* If we crossed page boundary, update LogwrtRqst.Write; if we crossed
1171+
* segment boundary, register that and wake up walwriter.
11581172
*/
11591173
if (StartPos /XLOG_BLCKSZ!=EndPos /XLOG_BLCKSZ)
11601174
{
1175+
XLogSegNoStartSeg;
1176+
XLogSegNoEndSeg;
1177+
1178+
XLByteToSeg(StartPos,StartSeg,wal_segment_size);
1179+
XLByteToSeg(EndPos,EndSeg,wal_segment_size);
1180+
1181+
/*
1182+
* Register our crossing the segment boundary if that occurred.
1183+
*
1184+
* Note that we did not use XLByteToPrevSeg() for determining the
1185+
* ending segment. This is so that a record that fits perfectly into
1186+
* the end of the segment causes the latter to get marked ready for
1187+
* archival immediately.
1188+
*/
1189+
if (StartSeg!=EndSeg&&XLogArchivingActive())
1190+
RegisterSegmentBoundary(EndSeg,EndPos);
1191+
1192+
/*
1193+
* Advance LogwrtRqst.Write so that it includes new block(s).
1194+
*
1195+
* We do this after registering the segment boundary so that the
1196+
* comparison with the flushed pointer below can use the latest value
1197+
* known globally.
1198+
*/
11611199
SpinLockAcquire(&XLogCtl->info_lck);
1162-
/* advance global request to include new block(s) */
11631200
if (XLogCtl->LogwrtRqst.Write<EndPos)
11641201
XLogCtl->LogwrtRqst.Write=EndPos;
11651202
/* update local result copy while I have the chance */
11661203
LogwrtResult=XLogCtl->LogwrtResult;
11671204
SpinLockRelease(&XLogCtl->info_lck);
1205+
1206+
/*
1207+
* There's a chance that the record was already flushed to disk and we
1208+
* missed marking segments as ready for archive. If this happens, we
1209+
* nudge the WALWriter, which will take care of notifying segments as
1210+
* needed.
1211+
*/
1212+
if (StartSeg!=EndSeg&&XLogArchivingActive()&&
1213+
LogwrtResult.Flush >=EndPos&&ProcGlobal->walwriterLatch)
1214+
SetLatch(ProcGlobal->walwriterLatch);
11681215
}
11691216

11701217
/*
11711218
* If this was an XLOG_SWITCH record, flush the record and the empty
1172-
* padding space that fills the rest of the segment, and perform
1173-
* end-of-segment actions (eg, notifying archiver).
1219+
* padding space that fills the rest of the segment.
11741220
*/
11751221
if (isLogSwitch)
11761222
{
@@ -2422,6 +2468,7 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
24222468

24232469
/* We should always be inside a critical section here */
24242470
Assert(CritSectionCount>0);
2471+
Assert(LWLockHeldByMe(WALWriteLock));
24252472

24262473
/*
24272474
* Update local LogwrtResult (caller probably did this already, but...)
@@ -2567,11 +2614,12 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
25672614
* later. Doing it here ensures that one and only one backend will
25682615
* perform this fsync.
25692616
*
2570-
* This is also the right place to notify the Archiver that the
2571-
* segment is ready to copy to archival storage, and to update the
2572-
* timer for archive_timeout, and to signal for a checkpoint if
2573-
* too many logfile segments have been used since the last
2574-
* checkpoint.
2617+
* If WAL archiving is active, we attempt to notify the archiver
2618+
* of any segments that are now ready for archival.
2619+
*
2620+
* This is also the right place to update the timer for
2621+
* archive_timeout and to signal for a checkpoint if too many
2622+
* logfile segments have been used since the last checkpoint.
25752623
*/
25762624
if (finishing_seg)
25772625
{
@@ -2583,7 +2631,7 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
25832631
LogwrtResult.Flush=LogwrtResult.Write;/* end of page */
25842632

25852633
if (XLogArchivingActive())
2586-
XLogArchiveNotifySeg(openLogSegNo);
2634+
NotifySegmentsReadyForArchive(LogwrtResult.Flush);
25872635

25882636
XLogCtl->lastSegSwitchTime= (pg_time_t)time(NULL);
25892637
XLogCtl->lastSegSwitchLSN=LogwrtResult.Flush;
@@ -2671,6 +2719,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
26712719
XLogCtl->LogwrtRqst.Flush=LogwrtResult.Flush;
26722720
SpinLockRelease(&XLogCtl->info_lck);
26732721
}
2722+
2723+
if (XLogArchivingActive())
2724+
NotifySegmentsReadyForArchive(LogwrtResult.Flush);
26742725
}
26752726

26762727
/*
@@ -4280,6 +4331,129 @@ ValidateXLOGDirectoryStructure(void)
42804331
}
42814332
}
42824333

4334+
/*
4335+
* RegisterSegmentBoundary
4336+
*
4337+
* WAL records that are split across a segment boundary require special
4338+
* treatment for archiving: the initial segment must not be archived until
4339+
* the end segment has been flushed, in case we crash before we have
4340+
* the chance to flush the end segment (because after recovery we would
4341+
* overwrite that WAL record with a different one, and so the file we
4342+
* archived no longer represents truth.) This also applies to streaming
4343+
* physical replication.
4344+
*
4345+
* To handle this, we keep track of the LSN of WAL records that cross
4346+
* segment boundaries. Two such are sufficient: the ones with the
4347+
* earliest and the latest end pointers we know about, since the flush
4348+
* position advances monotonically. WAL record writers register
4349+
* boundary-crossing records here, which is used by .ready file creation
4350+
* to delay until the end segment is known flushed.
4351+
*/
4352+
staticvoid
4353+
RegisterSegmentBoundary(XLogSegNoseg,XLogRecPtrendpos)
4354+
{
4355+
XLogSegNosegnoPG_USED_FOR_ASSERTS_ONLY;
4356+
4357+
/* verify caller computed segment number correctly */
4358+
AssertArg((XLByteToSeg(endpos,segno,wal_segment_size),segno==seg));
4359+
4360+
SpinLockAcquire(&XLogCtl->segtrack_lck);
4361+
4362+
/*
4363+
* If no segment boundaries are registered, store the new segment boundary
4364+
* in earliestSegBoundary. Otherwise, store the greater segment
4365+
* boundaries in latestSegBoundary.
4366+
*/
4367+
if (XLogCtl->earliestSegBoundary==MaxXLogSegNo)
4368+
{
4369+
XLogCtl->earliestSegBoundary=seg;
4370+
XLogCtl->earliestSegBoundaryEndPtr=endpos;
4371+
}
4372+
elseif (seg>XLogCtl->earliestSegBoundary&&
4373+
(XLogCtl->latestSegBoundary==MaxXLogSegNo||
4374+
seg>XLogCtl->latestSegBoundary))
4375+
{
4376+
XLogCtl->latestSegBoundary=seg;
4377+
XLogCtl->latestSegBoundaryEndPtr=endpos;
4378+
}
4379+
4380+
SpinLockRelease(&XLogCtl->segtrack_lck);
4381+
}
4382+
4383+
/*
4384+
* NotifySegmentsReadyForArchive
4385+
*
4386+
* Mark segments as ready for archival, given that it is safe to do so.
4387+
* This function is idempotent.
4388+
*/
4389+
void
4390+
NotifySegmentsReadyForArchive(XLogRecPtrflushRecPtr)
4391+
{
4392+
XLogSegNolatest_boundary_seg;
4393+
XLogSegNolast_notified;
4394+
XLogSegNoflushed_seg;
4395+
XLogSegNoseg;
4396+
boolkeep_latest;
4397+
4398+
XLByteToSeg(flushRecPtr,flushed_seg,wal_segment_size);
4399+
4400+
SpinLockAcquire(&XLogCtl->segtrack_lck);
4401+
4402+
if (XLogCtl->latestSegBoundary <=flushed_seg&&
4403+
XLogCtl->latestSegBoundaryEndPtr <=flushRecPtr)
4404+
{
4405+
latest_boundary_seg=XLogCtl->latestSegBoundary;
4406+
keep_latest= false;
4407+
}
4408+
elseif (XLogCtl->earliestSegBoundary <=flushed_seg&&
4409+
XLogCtl->earliestSegBoundaryEndPtr <=flushRecPtr)
4410+
{
4411+
latest_boundary_seg=XLogCtl->earliestSegBoundary;
4412+
keep_latest= true;
4413+
}
4414+
else
4415+
{
4416+
SpinLockRelease(&XLogCtl->segtrack_lck);
4417+
return;
4418+
}
4419+
4420+
last_notified=XLogCtl->lastNotifiedSeg;
4421+
4422+
/*
4423+
* Update shared memory and discard segment boundaries that are no longer
4424+
* needed.
4425+
*
4426+
* It is safe to update shared memory before we attempt to create the
4427+
* .ready files. If our calls to XLogArchiveNotifySeg() fail,
4428+
* RemoveOldXlogFiles() will retry it as needed.
4429+
*/
4430+
if (last_notified<latest_boundary_seg-1)
4431+
XLogCtl->lastNotifiedSeg=latest_boundary_seg-1;
4432+
4433+
if (keep_latest)
4434+
{
4435+
XLogCtl->earliestSegBoundary=XLogCtl->latestSegBoundary;
4436+
XLogCtl->earliestSegBoundaryEndPtr=XLogCtl->latestSegBoundaryEndPtr;
4437+
}
4438+
else
4439+
{
4440+
XLogCtl->earliestSegBoundary=MaxXLogSegNo;
4441+
XLogCtl->earliestSegBoundaryEndPtr=InvalidXLogRecPtr;
4442+
}
4443+
4444+
XLogCtl->latestSegBoundary=MaxXLogSegNo;
4445+
XLogCtl->latestSegBoundaryEndPtr=InvalidXLogRecPtr;
4446+
4447+
SpinLockRelease(&XLogCtl->segtrack_lck);
4448+
4449+
/*
4450+
* Notify archiver about segments that are ready for archival (by creating
4451+
* the corresponding .ready files).
4452+
*/
4453+
for (seg=last_notified+1;seg<latest_boundary_seg;seg++)
4454+
XLogArchiveNotifySeg(seg);
4455+
}
4456+
42834457
/*
42844458
* Remove previous backup history files. This also retries creation of
42854459
* .ready files for any backup history files for which XLogArchiveNotify
@@ -5181,8 +5355,16 @@ XLOGShmemInit(void)
51815355

51825356
SpinLockInit(&XLogCtl->Insert.insertpos_lck);
51835357
SpinLockInit(&XLogCtl->info_lck);
5358+
SpinLockInit(&XLogCtl->segtrack_lck);
51845359
SpinLockInit(&XLogCtl->ulsn_lck);
51855360
InitSharedLatch(&XLogCtl->recoveryWakeupLatch);
5361+
5362+
/* Initialize stuff for marking segments as ready for archival. */
5363+
XLogCtl->lastNotifiedSeg=MaxXLogSegNo;
5364+
XLogCtl->earliestSegBoundary=MaxXLogSegNo;
5365+
XLogCtl->earliestSegBoundaryEndPtr=InvalidXLogRecPtr;
5366+
XLogCtl->latestSegBoundary=MaxXLogSegNo;
5367+
XLogCtl->latestSegBoundaryEndPtr=InvalidXLogRecPtr;
51865368
}
51875369

51885370
/*
@@ -7706,6 +7888,20 @@ StartupXLOG(void)
77067888
XLogCtl->LogwrtRqst.Write=EndOfLog;
77077889
XLogCtl->LogwrtRqst.Flush=EndOfLog;
77087890

7891+
/*
7892+
* Initialize XLogCtl->lastNotifiedSeg to the previous WAL file.
7893+
*/
7894+
if (XLogArchivingActive())
7895+
{
7896+
XLogSegNoEndOfLogSeg;
7897+
7898+
XLByteToSeg(EndOfLog,EndOfLogSeg,wal_segment_size);
7899+
7900+
SpinLockAcquire(&XLogCtl->segtrack_lck);
7901+
XLogCtl->lastNotifiedSeg=EndOfLogSeg-1;
7902+
SpinLockRelease(&XLogCtl->segtrack_lck);
7903+
}
7904+
77097905
/*
77107906
* Update full_page_writes in shared memory and write an XLOG_FPW_CHANGE
77117907
* record before resource manager writes cleanup WAL records or checkpoint

‎src/backend/postmaster/walwriter.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,13 @@ WalWriterMain(void)
234234

235235
HandleMainLoopInterrupts();
236236

237+
/*
238+
* Notify the archiver of any WAL segments that are ready. We do this
239+
* here to handle a race condition where WAL is flushed to disk prior
240+
* to registering the segment boundary.
241+
*/
242+
NotifySegmentsReadyForArchive(GetFlushRecPtr());
243+
237244
/*
238245
* Do what we're here for; then, if XLogBackgroundFlush() found useful
239246
* work to do, reset hibernation counter.

‎src/include/access/xlog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ extern XLogRecPtr GetInsertRecPtr(void);
339339
externXLogRecPtrGetFlushRecPtr(void);
340340
externXLogRecPtrGetLastImportantRecPtr(void);
341341
externvoidRemovePromoteSignalFiles(void);
342+
externvoidNotifySegmentsReadyForArchive(XLogRecPtrflushRecPtr);
342343

343344
externboolPromoteIsTriggered(void);
344345
externboolCheckPromoteSignal(void);

‎src/include/access/xlogdefs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ typedef uint64 XLogRecPtr;
3939
* XLogSegNo - physical log file sequence number.
4040
*/
4141
typedefuint64XLogSegNo;
42+
#defineMaxXLogSegNo((XLogSegNo) 0xFFFFFFFFFFFFFFFF)
4243

4344
/*
4445
* TimeLineID (TLI) - identifies different database histories to prevent

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp