Skip to content

Commit cea8f2c

Browse files
committed
Keep WAL segments by the flushed value of the slot's restart LSN
The patch fixes the issue with the unexpected removal of old WAL segments after checkpoint, followed by an immediate restart. The issue occurs when a slot is advanced after the start of the checkpoint and before old WAL segments are removed at the end of the checkpoint. The idea of the patch is to get the minimal restart_lsn at the beginning of checkpoint (or restart point) creation and use this value when calculating the oldest LSN for WAL segments removal at the end of checkpoint. This idea was proposed by Tomas Vondra in the discussion. Unlike 291221c46575, this fix doesn't affect ABI and is intended for back branches. Discussion: https://postgr.es/m/flat/1d12d2-67235980-35-19a406a0%4063439497 Author: Vitaly Davydov <v.davydov@postgrespro.ru> Reviewed-by: Tomas Vondra <tomas@vondra.me> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Reviewed-by: Amit Kapila <amit.kapila16@gmail.com> Backpatch-through: 13
1 parent c7f25fe commit cea8f2c

File tree

3 files changed

+60
-9
lines changed

3 files changed

+60
-9
lines changed

src/backend/access/transam/xlog.c

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,8 @@ static XLogRecPtr CreateOverwriteContrecordRecord(XLogRecPtr aborted_lsn,
650650
XLogRecPtr pagePtr,
651651
TimeLineID newTLI);
652652
static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags);
653-
static void KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo);
653+
static void KeepLogSeg(XLogRecPtr recptr, XLogRecPtr slotsMinLSN,
654+
XLogSegNo *logSegNo);
654655
static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
655656

656657
static void AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli,
@@ -6492,6 +6493,7 @@ CreateCheckPoint(int flags)
64926493
VirtualTransactionId *vxids;
64936494
int nvxids;
64946495
int oldXLogAllowed = 0;
6496+
XLogRecPtr slotsMinReqLSN;
64956497

64966498
/*
64976499
* An end-of-recovery checkpoint is really a shutdown checkpoint, just
@@ -6693,6 +6695,15 @@ CreateCheckPoint(int flags)
66936695
*/
66946696
END_CRIT_SECTION();
66956697

6698+
/*
6699+
* Get the current minimum LSN to be used later in the WAL segment
6700+
* cleanup. We may clean up only WAL segments, which are not needed
6701+
* according to synchronized LSNs of replication slots. The slot's LSN
6702+
* might be advanced concurrently, so we call this before
6703+
* CheckPointReplicationSlots() synchronizes replication slots.
6704+
*/
6705+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
6706+
66966707
/*
66976708
* In some cases there are groups of actions that must all occur on one
66986709
* side or the other of a checkpoint record. Before flushing the
@@ -6858,17 +6869,25 @@ CreateCheckPoint(int flags)
68586869
* prevent the disk holding the xlog from growing full.
68596870
*/
68606871
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
6861-
KeepLogSeg(recptr, &_logSegNo);
6872+
KeepLogSeg(recptr, slotsMinReqLSN, &_logSegNo);
68626873
if (InvalidateObsoleteReplicationSlots(RS_INVAL_WAL_REMOVED,
68636874
_logSegNo, InvalidOid,
68646875
InvalidTransactionId))
68656876
{
6877+
/*
6878+
* Recalculate the current minimum LSN to be used in the WAL segment
6879+
* cleanup. Then, we must synchronize the replication slots again in
6880+
* order to make this LSN safe to use.
6881+
*/
6882+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
6883+
CheckPointReplicationSlots();
6884+
68666885
/*
68676886
* Some slots have been invalidated; recalculate the old-segment
68686887
* horizon, starting again from RedoRecPtr.
68696888
*/
68706889
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
6871-
KeepLogSeg(recptr, &_logSegNo);
6890+
KeepLogSeg(recptr, slotsMinReqLSN, &_logSegNo);
68726891
}
68736892
_logSegNo--;
68746893
RemoveOldXlogFiles(_logSegNo, RedoRecPtr, recptr,
@@ -7140,6 +7159,7 @@ CreateRestartPoint(int flags)
71407159
XLogRecPtr endptr;
71417160
XLogSegNo _logSegNo;
71427161
TimestampTz xtime;
7162+
XLogRecPtr slotsMinReqLSN;
71437163

71447164
/* Concurrent checkpoint/restartpoint cannot happen */
71457165
Assert(!IsUnderPostmaster || MyBackendType == B_CHECKPOINTER);
@@ -7222,6 +7242,15 @@ CreateRestartPoint(int flags)
72227242
MemSet(&CheckpointStats, 0, sizeof(CheckpointStats));
72237243
CheckpointStats.ckpt_start_t = GetCurrentTimestamp();
72247244

7245+
/*
7246+
* Get the current minimum LSN to be used later in the WAL segment
7247+
* cleanup. We may clean up only WAL segments, which are not needed
7248+
* according to synchronized LSNs of replication slots. The slot's LSN
7249+
* might be advanced concurrently, so we call this before
7250+
* CheckPointReplicationSlots() synchronizes replication slots.
7251+
*/
7252+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7253+
72257254
if (log_checkpoints)
72267255
LogCheckpointStart(flags, true);
72277256

@@ -7304,17 +7333,25 @@ CreateRestartPoint(int flags)
73047333
receivePtr = GetWalRcvFlushRecPtr(NULL, NULL);
73057334
replayPtr = GetXLogReplayRecPtr(&replayTLI);
73067335
endptr = (receivePtr < replayPtr) ? replayPtr : receivePtr;
7307-
KeepLogSeg(endptr, &_logSegNo);
7336+
KeepLogSeg(endptr, slotsMinReqLSN, &_logSegNo);
73087337
if (InvalidateObsoleteReplicationSlots(RS_INVAL_WAL_REMOVED,
73097338
_logSegNo, InvalidOid,
73107339
InvalidTransactionId))
73117340
{
7341+
/*
7342+
* Recalculate the current minimum LSN to be used in the WAL segment
7343+
* cleanup. Then, we must synchronize the replication slots again in
7344+
* order to make this LSN safe to use.
7345+
*/
7346+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7347+
CheckPointReplicationSlots();
7348+
73127349
/*
73137350
* Some slots have been invalidated; recalculate the old-segment
73147351
* horizon, starting again from RedoRecPtr.
73157352
*/
73167353
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
7317-
KeepLogSeg(endptr, &_logSegNo);
7354+
KeepLogSeg(endptr, slotsMinReqLSN, &_logSegNo);
73187355
}
73197356
_logSegNo--;
73207357

@@ -7409,6 +7446,7 @@ GetWALAvailability(XLogRecPtr targetLSN)
74097446
XLogSegNo oldestSegMaxWalSize; /* oldest segid kept by max_wal_size */
74107447
XLogSegNo oldestSlotSeg; /* oldest segid kept by slot */
74117448
uint64 keepSegs;
7449+
XLogRecPtr slotsMinReqLSN;
74127450

74137451
/*
74147452
* slot does not reserve WAL. Either deactivated, or has never been active
@@ -7422,8 +7460,9 @@ GetWALAvailability(XLogRecPtr targetLSN)
74227460
* oldestSlotSeg to the current segment.
74237461
*/
74247462
currpos = GetXLogWriteRecPtr();
7463+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
74257464
XLByteToSeg(currpos, oldestSlotSeg, wal_segment_size);
7426-
KeepLogSeg(currpos, &oldestSlotSeg);
7465+
KeepLogSeg(currpos, slotsMinReqLSN, &oldestSlotSeg);
74277466

74287467
/*
74297468
* Find the oldest extant segment file. We get 1 until checkpoint removes
@@ -7484,7 +7523,7 @@ GetWALAvailability(XLogRecPtr targetLSN)
74847523
* invalidation is optionally done here, instead.
74857524
*/
74867525
static void
7487-
KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo)
7526+
KeepLogSeg(XLogRecPtr recptr, XLogRecPtr slotsMinReqLSN, XLogSegNo *logSegNo)
74887527
{
74897528
XLogSegNo currSegNo;
74907529
XLogSegNo segno;
@@ -7497,7 +7536,7 @@ KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo)
74977536
* Calculate how many segments are kept by slots first, adjusting for
74987537
* max_slot_wal_keep_size.
74997538
*/
7500-
keep = XLogGetReplicationSlotMinimumLSN();
7539+
keep = slotsMinReqLSN;
75017540
if (keep != InvalidXLogRecPtr && keep < recptr)
75027541
{
75037542
XLByteToSeg(keep, segno, wal_segment_size);

src/backend/replication/logical/logical.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1880,7 +1880,15 @@ LogicalConfirmReceivedLocation(XLogRecPtr lsn)
18801880

18811881
SpinLockRelease(&MyReplicationSlot->mutex);
18821882

1883-
/* first write new xmin to disk, so we know what's up after a crash */
1883+
/*
1884+
* First, write new xmin and restart_lsn to disk so we know what's up
1885+
* after a crash. Even when we do this, the checkpointer can see the
1886+
* updated restart_lsn value in the shared memory; then, a crash can
1887+
* happen before we manage to write that value to the disk. Thus,
1888+
* checkpointer still needs to make special efforts to keep WAL
1889+
* segments required by the restart_lsn written to the disk. See
1890+
* CreateCheckPoint() and CreateRestartPoint() for details.
1891+
*/
18841892
if (updated_xmin || updated_restart)
18851893
{
18861894
ReplicationSlotMarkDirty();

src/backend/replication/walsender.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,6 +2071,10 @@ PhysicalConfirmReceivedLocation(XLogRecPtr lsn)
20712071
* be energy wasted - the worst thing lost information could cause here is
20722072
* to give wrong information in a statistics view - we'll just potentially
20732073
* be more conservative in removing files.
2074+
*
2075+
* Checkpointer makes special efforts to keep the WAL segments required by
2076+
* the restart_lsn written to the disk. See CreateCheckPoint() and
2077+
* CreateRestartPoint() for details.
20742078
*/
20752079
}
20762080

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