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

Commit aa1a747

Browse files
akorotkovCommitfest Bot
authored and
Commitfest Bot
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. 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> Backpatch-through: 13
1 parent b006bcd commit aa1a747

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
@@ -677,7 +677,8 @@ static XLogRecPtr CreateOverwriteContrecordRecord(XLogRecPtr aborted_lsn,
677677
XLogRecPtr pagePtr,
678678
TimeLineID newTLI);
679679
static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags);
680-
static void KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo);
680+
static void KeepLogSeg(XLogRecPtr recptr, XLogRecPtr slotsMinLSN,
681+
XLogSegNo *logSegNo);
681682
static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
682683

683684
static void AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli,
@@ -7087,6 +7088,7 @@ CreateCheckPoint(int flags)
70877088
VirtualTransactionId *vxids;
70887089
int nvxids;
70897090
int oldXLogAllowed = 0;
7091+
XLogRecPtr slotsMinReqLSN;
70907092

70917093
/*
70927094
* An end-of-recovery checkpoint is really a shutdown checkpoint, just
@@ -7315,6 +7317,15 @@ CreateCheckPoint(int flags)
73157317
*/
73167318
END_CRIT_SECTION();
73177319

7320+
/*
7321+
* Get the current minimum LSN to be used later in the WAL segment
7322+
* cleanup. We may clean up only WAL segments, which are not needed
7323+
* according to synchronized LSNs of replication slots. The slot's LSN
7324+
* might be advanced concurrently, so we call this before
7325+
* CheckPointReplicationSlots() synchronizes replication slots.
7326+
*/
7327+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7328+
73187329
/*
73197330
* In some cases there are groups of actions that must all occur on one
73207331
* side or the other of a checkpoint record. Before flushing the
@@ -7503,17 +7514,25 @@ CreateCheckPoint(int flags)
75037514
* prevent the disk holding the xlog from growing full.
75047515
*/
75057516
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
7506-
KeepLogSeg(recptr, &_logSegNo);
7517+
KeepLogSeg(recptr, slotsMinReqLSN, &_logSegNo);
75077518
if (InvalidateObsoleteReplicationSlots(RS_INVAL_WAL_REMOVED | RS_INVAL_IDLE_TIMEOUT,
75087519
_logSegNo, InvalidOid,
75097520
InvalidTransactionId))
75107521
{
7522+
/*
7523+
* Recalculate the current minimum LSN to be used in the WAL segment
7524+
* cleanup. Then, we must synchronize the replication slots again in
7525+
* order to make this LSN safe to use.
7526+
*/
7527+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7528+
CheckPointReplicationSlots(shutdown);
7529+
75117530
/*
75127531
* Some slots have been invalidated; recalculate the old-segment
75137532
* horizon, starting again from RedoRecPtr.
75147533
*/
75157534
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
7516-
KeepLogSeg(recptr, &_logSegNo);
7535+
KeepLogSeg(recptr, slotsMinReqLSN, &_logSegNo);
75177536
}
75187537
_logSegNo--;
75197538
RemoveOldXlogFiles(_logSegNo, RedoRecPtr, recptr,
@@ -7788,6 +7807,7 @@ CreateRestartPoint(int flags)
77887807
XLogRecPtr endptr;
77897808
XLogSegNo _logSegNo;
77907809
TimestampTz xtime;
7810+
XLogRecPtr slotsMinReqLSN;
77917811

77927812
/* Concurrent checkpoint/restartpoint cannot happen */
77937813
Assert(!IsUnderPostmaster || MyBackendType == B_CHECKPOINTER);
@@ -7870,6 +7890,15 @@ CreateRestartPoint(int flags)
78707890
MemSet(&CheckpointStats, 0, sizeof(CheckpointStats));
78717891
CheckpointStats.ckpt_start_t = GetCurrentTimestamp();
78727892

7893+
/*
7894+
* Get the current minimum LSN to be used later in the WAL segment
7895+
* cleanup. We may clean up only WAL segments, which are not needed
7896+
* according to synchronized LSNs of replication slots. The slot's LSN
7897+
* might be advanced concurrently, so we call this before
7898+
* CheckPointReplicationSlots() synchronizes replication slots.
7899+
*/
7900+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
7901+
78737902
if (log_checkpoints)
78747903
LogCheckpointStart(flags, true);
78757904

@@ -7958,17 +7987,25 @@ CreateRestartPoint(int flags)
79587987
receivePtr = GetWalRcvFlushRecPtr(NULL, NULL);
79597988
replayPtr = GetXLogReplayRecPtr(&replayTLI);
79607989
endptr = (receivePtr < replayPtr) ? replayPtr : receivePtr;
7961-
KeepLogSeg(endptr, &_logSegNo);
7990+
KeepLogSeg(endptr, slotsMinReqLSN, &_logSegNo);
79627991
if (InvalidateObsoleteReplicationSlots(RS_INVAL_WAL_REMOVED | RS_INVAL_IDLE_TIMEOUT,
79637992
_logSegNo, InvalidOid,
79647993
InvalidTransactionId))
79657994
{
7995+
/*
7996+
* Recalculate the current minimum LSN to be used in the WAL segment
7997+
* cleanup. Then, we must synchronize the replication slots again in
7998+
* order to make this LSN safe to use.
7999+
*/
8000+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
8001+
CheckPointReplicationSlots(flags & CHECKPOINT_IS_SHUTDOWN);
8002+
79668003
/*
79678004
* Some slots have been invalidated; recalculate the old-segment
79688005
* horizon, starting again from RedoRecPtr.
79698006
*/
79708007
XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
7971-
KeepLogSeg(endptr, &_logSegNo);
8008+
KeepLogSeg(endptr, slotsMinReqLSN, &_logSegNo);
79728009
}
79738010
_logSegNo--;
79748011

@@ -8063,6 +8100,7 @@ GetWALAvailability(XLogRecPtr targetLSN)
80638100
XLogSegNo oldestSegMaxWalSize; /* oldest segid kept by max_wal_size */
80648101
XLogSegNo oldestSlotSeg; /* oldest segid kept by slot */
80658102
uint64 keepSegs;
8103+
XLogRecPtr slotsMinReqLSN;
80668104

80678105
/*
80688106
* slot does not reserve WAL. Either deactivated, or has never been active
@@ -8076,8 +8114,9 @@ GetWALAvailability(XLogRecPtr targetLSN)
80768114
* oldestSlotSeg to the current segment.
80778115
*/
80788116
currpos = GetXLogWriteRecPtr();
8117+
slotsMinReqLSN = XLogGetReplicationSlotMinimumLSN();
80798118
XLByteToSeg(currpos, oldestSlotSeg, wal_segment_size);
8080-
KeepLogSeg(currpos, &oldestSlotSeg);
8119+
KeepLogSeg(currpos, slotsMinReqLSN, &oldestSlotSeg);
80818120

80828121
/*
80838122
* Find the oldest extant segment file. We get 1 until checkpoint removes
@@ -8138,7 +8177,7 @@ GetWALAvailability(XLogRecPtr targetLSN)
81388177
* invalidation is optionally done here, instead.
81398178
*/
81408179
static void
8141-
KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo)
8180+
KeepLogSeg(XLogRecPtr recptr, XLogRecPtr slotsMinReqLSN, XLogSegNo *logSegNo)
81428181
{
81438182
XLogSegNo currSegNo;
81448183
XLogSegNo segno;
@@ -8151,7 +8190,7 @@ KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo)
81518190
* Calculate how many segments are kept by slots first, adjusting for
81528191
* max_slot_wal_keep_size.
81538192
*/
8154-
keep = XLogGetReplicationSlotMinimumLSN();
8193+
keep = slotsMinReqLSN;
81558194
if (keep != InvalidXLogRecPtr && keep < recptr)
81568195
{
81578196
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
@@ -1878,7 +1878,15 @@ LogicalConfirmReceivedLocation(XLogRecPtr lsn)
18781878

18791879
SpinLockRelease(&MyReplicationSlot->mutex);
18801880

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

src/backend/replication/walsender.c

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

0 commit comments

Comments
 (0)