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

Commit e1c26ab

Browse files
committed
Fix the truncation logic of the OldSerXid SLRU mechanism. We can't pass
SimpleLruTruncate() a page number that's "in the future", because it will issue a warning and refuse to truncate anything. Instead, we leave behind the latest segment. If the slru is not needed before XID wrap-around, the segment will appear as new again, and not be cleaned up until it gets old enough again. That's a bit unpleasant, but better than not cleaning up anything. Also, fix broken calculation to check and warn if the span of the OldSerXid SLRU is getting too large to fit in the 64k SLRU pages that we have available. It was not XID wraparound aware. Kevin Grittner and me.
1 parent 6560407 commit e1c26ab

File tree

1 file changed

+43
-19
lines changed

1 file changed

+43
-19
lines changed

src/backend/storage/lmgr/predicate.c

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ typedef struct OldSerXidControlData
330330
int headPage; /* newest initialized page */
331331
TransactionId headXid; /* newest valid Xid in the SLRU */
332332
TransactionId tailXid; /* oldest xmin we might be interested in */
333-
bool warningIssued;
333+
bool warningIssued; /* have we issued SLRU wrap-around warning? */
334334
} OldSerXidControlData;
335335

336336
typedef struct OldSerXidControlData *OldSerXidControl;
@@ -738,7 +738,6 @@ OldSerXidAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
738738
int targetPage;
739739
int slotno;
740740
int firstZeroPage;
741-
int xidSpread;
742741
bool isNewPage;
743742

744743
Assert(TransactionIdIsValid(xid));
@@ -779,18 +778,45 @@ OldSerXidAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
779778
if (isNewPage)
780779
oldSerXidControl->headPage = targetPage;
781780

782-
xidSpread = (((uint32) xid) - ((uint32) tailXid));
781+
/*
782+
* Give a warning if we're about to run out of SLRU pages.
783+
*
784+
* slru.c has a maximum of 64k segments, with 32 (SLRU_PAGES_PER_SEGMENT)
785+
* pages each. We need to store a 64-bit integer for each Xid, and with
786+
* default 8k block size, 65536*32 pages is only enough to cover 2^30
787+
* XIDs. If we're about to hit that limit and wrap around, warn the user.
788+
*
789+
* To avoid spamming the user, we only give one warning when we've used 1
790+
* billion XIDs, and stay silent until the situation is fixed and the
791+
* number of XIDs used falls below 800 million again.
792+
*
793+
* XXX: We have no safeguard to actually *prevent* the wrap-around,
794+
* though. All you get is a warning.
795+
*/
783796
if (oldSerXidControl->warningIssued)
784797
{
785-
if (xidSpread < 800000000)
798+
TransactionId lowWatermark;
799+
800+
lowWatermark = tailXid + 800000000;
801+
if (lowWatermark < FirstNormalTransactionId)
802+
lowWatermark = FirstNormalTransactionId;
803+
if (TransactionIdPrecedes(xid, lowWatermark))
786804
oldSerXidControl->warningIssued = false;
787805
}
788-
else if (xidSpread >= 1000000000)
806+
else
789807
{
790-
oldSerXidControl->warningIssued = true;
791-
ereport(WARNING,
792-
(errmsg("memory for serializable conflict tracking is nearly exhausted"),
793-
errhint("There may be an idle transaction or a forgotten prepared transaction causing this.")));
808+
TransactionId highWatermark;
809+
810+
highWatermark = tailXid + 1000000000;
811+
if (highWatermark < FirstNormalTransactionId)
812+
highWatermark = FirstNormalTransactionId;
813+
if (TransactionIdFollows(xid, highWatermark))
814+
{
815+
oldSerXidControl->warningIssued = true;
816+
ereport(WARNING,
817+
(errmsg("memory for serializable conflict tracking is nearly exhausted"),
818+
errhint("There may be an idle transaction or a forgotten prepared transaction causing this.")));
819+
}
794820
}
795821

796822
if (isNewPage)
@@ -931,18 +957,16 @@ CheckPointPredicate(void)
931957
else
932958
{
933959
/*
934-
* The SLRU is no longer needed. Truncate everything. If we try to
935-
* leave the head page around to avoid re-zeroing it, we might not use
936-
* the SLRU again until we're past the wrap-around point, which makes
937-
* SLRU unhappy.
960+
* The SLRU is no longer needed. Truncate to head before we set head
961+
* invalid.
938962
*
939-
* While the API asks you to specify truncation by page, it silently
940-
* ignores the request unless the specified page is in a segment past
941-
* some allocated portion of the SLRU. We don't care which page in a
942-
* later segment we hit, so just add the number of pages per segment
943-
* to the head page to land us *somewhere* in the next segment.
963+
* XXX: It's possible that the SLRU is not needed again until XID
964+
* wrap-around has happened, so that the segment containing headPage
965+
* that we leave behind will appear to be new again. In that case it
966+
* won't be removed until XID horizon advances enough to make it
967+
* current again.
944968
*/
945-
tailPage = oldSerXidControl->headPage + SLRU_PAGES_PER_SEGMENT;
969+
tailPage = oldSerXidControl->headPage;
946970
oldSerXidControl->headPage = -1;
947971
}
948972

0 commit comments

Comments
 (0)