Skip to content

Commit 2090edc

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 40aa5dd commit 2090edc

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
@@ -667,7 +667,8 @@ static XLogRecPtr CreateOverwriteContrecordRecord(XLogRecPtr aborted_lsn,
667667
XLogRecPtr pagePtr,
668668
TimeLineID newTLI);
669669
static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags);
670-
static void KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo);
670+
static void KeepLogSeg(XLogRecPtr recptr, XLogRecPtr slotsMinLSN,
671+
XLogSegNo *logSegNo);
671672
static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
672673

673674
static void AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli,
@@ -6891,6 +6892,7 @@ CreateCheckPoint(int flags)
68916892
VirtualTransactionId *vxids;
68926893
int nvxids;
68936894
int oldXLogAllowed = 0;
6895+
XLogRecPtr slotsMinReqLSN;
68946896

68956897
/*
68966898
* An end-of-recovery checkpoint is really a shutdown checkpoint, just
@@ -7119,6 +7121,15 @@ CreateCheckPoint(int flags)
71197121
*/
71207122
END_CRIT_SECTION();
71217123

7124+
/*
7125+
* Get the current minimum LSN to be used later in the WAL segment
7126+
* cleanup. We may clean up only WAL segments, which are not needed
7127+
* according to synchronized LSNs of replication slots. The slot's LSN
7128+
* might be advanced concurrently, so we call this before
7129+
* CheckPointReplicationSlots() synchronizes replication slots.
7130+
*/
7131+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7132+
71227133
/*
71237134
* In some cases there are groups of actions that must all occur on one
71247135
* side or the other of a checkpoint record. Before flushing the
@@ -7307,17 +7318,25 @@ CreateCheckPoint(int flags)
73077318
* prevent the disk holding the xlog from growing full.
73087319
*/
73097320
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
7310-
KeepLogSeg(recptr, &_logSegNo);
7321+
KeepLogSeg(recptr, slotsMinReqLSN, &_logSegNo);
73117322
if (InvalidateObsoleteReplicationSlots(RS_INVAL_WAL_REMOVED,
73127323
_logSegNo, InvalidOid,
73137324
InvalidTransactionId))
73147325
{
7326+
/*
7327+
* Recalculate the current minimum LSN to be used in the WAL segment
7328+
* cleanup. Then, we must synchronize the replication slots again in
7329+
* order to make this LSN safe to use.
7330+
*/
7331+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7332+
CheckPointReplicationSlots(shutdown);
7333+
73157334
/*
73167335
* Some slots have been invalidated; recalculate the old-segment
73177336
* horizon, starting again from RedoRecPtr.
73187337
*/
73197338
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
7320-
KeepLogSeg(recptr, &_logSegNo);
7339+
KeepLogSeg(recptr, slotsMinReqLSN, &_logSegNo);
73217340
}
73227341
_logSegNo--;
73237342
RemoveOldXlogFiles(_logSegNo, RedoRecPtr, recptr,
@@ -7590,6 +7609,7 @@ CreateRestartPoint(int flags)
75907609
XLogRecPtr endptr;
75917610
XLogSegNo _logSegNo;
75927611
TimestampTz xtime;
7612+
XLogRecPtr slotsMinReqLSN;
75937613

75947614
/* Concurrent checkpoint/restartpoint cannot happen */
75957615
Assert(!IsUnderPostmaster || MyBackendType == B_CHECKPOINTER);
@@ -7672,6 +7692,15 @@ CreateRestartPoint(int flags)
76727692
MemSet(&CheckpointStats, 0, sizeof(CheckpointStats));
76737693
CheckpointStats.ckpt_start_t = GetCurrentTimestamp();
76747694

7695+
/*
7696+
* Get the current minimum LSN to be used later in the WAL segment
7697+
* cleanup. We may clean up only WAL segments, which are not needed
7698+
* according to synchronized LSNs of replication slots. The slot's LSN
7699+
* might be advanced concurrently, so we call this before
7700+
* CheckPointReplicationSlots() synchronizes replication slots.
7701+
*/
7702+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7703+
76757704
if (log_checkpoints)
76767705
LogCheckpointStart(flags, true);
76777706

@@ -7760,17 +7789,25 @@ CreateRestartPoint(int flags)
77607789
receivePtr = GetWalRcvFlushRecPtr(NULL, NULL);
77617790
replayPtr = GetXLogReplayRecPtr(&replayTLI);
77627791
endptr = (receivePtr < replayPtr) ? replayPtr : receivePtr;
7763-
KeepLogSeg(endptr, &_logSegNo);
7792+
KeepLogSeg(endptr, slotsMinReqLSN, &_logSegNo);
77647793
if (InvalidateObsoleteReplicationSlots(RS_INVAL_WAL_REMOVED,
77657794
_logSegNo, InvalidOid,
77667795
InvalidTransactionId))
77677796
{
7797+
/*
7798+
* Recalculate the current minimum LSN to be used in the WAL segment
7799+
* cleanup. Then, we must synchronize the replication slots again in
7800+
* order to make this LSN safe to use.
7801+
*/
7802+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7803+
CheckPointReplicationSlots(flags & CHECKPOINT_IS_SHUTDOWN);
7804+
77687805
/*
77697806
* Some slots have been invalidated; recalculate the old-segment
77707807
* horizon, starting again from RedoRecPtr.
77717808
*/
77727809
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
7773-
KeepLogSeg(endptr, &_logSegNo);
7810+
KeepLogSeg(endptr, slotsMinReqLSN, &_logSegNo);
77747811
}
77757812
_logSegNo--;
77767813

@@ -7865,6 +7902,7 @@ GetWALAvailability(XLogRecPtr targetLSN)
78657902
XLogSegNo oldestSegMaxWalSize; /* oldest segid kept by max_wal_size */
78667903
XLogSegNo oldestSlotSeg; /* oldest segid kept by slot */
78677904
uint64 keepSegs;
7905+
XLogRecPtr slotsMinReqLSN;
78687906

78697907
/*
78707908
* slot does not reserve WAL. Either deactivated, or has never been active
@@ -7878,8 +7916,9 @@ GetWALAvailability(XLogRecPtr targetLSN)
78787916
* oldestSlotSeg to the current segment.
78797917
*/
78807918
currpos = GetXLogWriteRecPtr();
7919+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
78817920
XLByteToSeg(currpos, oldestSlotSeg, wal_segment_size);
7882-
KeepLogSeg(currpos, &oldestSlotSeg);
7921+
KeepLogSeg(currpos, slotsMinReqLSN, &oldestSlotSeg);
78837922

78847923
/*
78857924
* Find the oldest extant segment file. We get 1 until checkpoint removes
@@ -7940,7 +7979,7 @@ GetWALAvailability(XLogRecPtr targetLSN)
79407979
* invalidation is optionally done here, instead.
79417980
*/
79427981
static void
7943-
KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo)
7982+
KeepLogSeg(XLogRecPtr recptr, XLogRecPtr slotsMinReqLSN, XLogSegNo *logSegNo)
79447983
{
79457984
XLogSegNo currSegNo;
79467985
XLogSegNo segno;
@@ -7953,7 +7992,7 @@ KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo)
79537992
* Calculate how many segments are kept by slots first, adjusting for
79547993
* max_slot_wal_keep_size.
79557994
*/
7956-
keep = XLogGetReplicationSlotMinimumLSN();
7995+
keep = slotsMinReqLSN;
79577996
if (keep != InvalidXLogRecPtr && keep < recptr)
79587997
{
79597998
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
@@ -1897,7 +1897,15 @@ LogicalConfirmReceivedLocation(XLogRecPtr lsn)
18971897

18981898
SpinLockRelease(&MyReplicationSlot->mutex);
18991899

1900-
/* first write new xmin to disk, so we know what's up after a crash */
1900+
/*
1901+
* First, write new xmin and restart_lsn to disk so we know what's up
1902+
* after a crash. Even when we do this, the checkpointer can see the
1903+
* updated restart_lsn value in the shared memory; then, a crash can
1904+
* happen before we manage to write that value to the disk. Thus,
1905+
* checkpointer still needs to make special efforts to keep WAL
1906+
* segments required by the restart_lsn written to the disk. See
1907+
* CreateCheckPoint() and CreateRestartPoint() for details.
1908+
*/
19011909
if (updated_xmin || updated_restart)
19021910
{
19031911
ReplicationSlotMarkDirty();

src/backend/replication/walsender.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2392,6 +2392,10 @@ PhysicalConfirmReceivedLocation(XLogRecPtr lsn)
23922392
* be energy wasted - the worst thing lost information could cause here is
23932393
* to give wrong information in a statistics view - we'll just potentially
23942394
* be more conservative in removing files.
2395+
*
2396+
* Checkpointer makes special efforts to keep the WAL segments required by
2397+
* the restart_lsn written to the disk. See CreateCheckPoint() and
2398+
* CreateRestartPoint() for details.
23952399
*/
23962400
}
23972401

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