1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Truncate predicate lock manager's SLRU lazily at checkpoint. That's safer

than doing it aggressively whenever the tail-XID pointer is advanced, because
this way we don't need to do it while holding SerializableXactHashLock.

This also fixes bug  spotted by YAMAMOTO Takashi, and removes an
obsolete comment spotted by Kevin Grittner.
This commit is contained in:
Heikki Linnakangas
2011-03-08 12:07:29 +02:00
parent 804d13adfd
commit 4cd3fb6e12
3 changed files with 69 additions and 52 deletions
src
backend
access
transam
storage
include

@ -48,6 +48,7 @@
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/latch.h" #include "storage/latch.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
#include "storage/predicate.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/reinit.h" #include "storage/reinit.h"
#include "storage/smgr.h" #include "storage/smgr.h"
@ -7875,6 +7876,7 @@ CheckPointGuts(XLogRecPtr checkPointRedo, int flags)
CheckPointCLOG(); CheckPointCLOG();
CheckPointSUBTRANS(); CheckPointSUBTRANS();
CheckPointMultiXact(); CheckPointMultiXact();
CheckPointPredicate();
CheckPointRelationMap(); CheckPointRelationMap();
CheckPointBuffers(flags); /* performs all required fsyncs */ CheckPointBuffers(flags); /* performs all required fsyncs */
/* We deliberately delay 2PC checkpointing as long as possible */ /* We deliberately delay 2PC checkpointing as long as possible */

@ -323,10 +323,9 @@ static SlruCtlData OldSerXidSlruCtlData;
typedef struct OldSerXidControlData typedef struct OldSerXidControlData
{ {
int headPage; int headPage; /* newest initialized page */
int tailSegment; TransactionId headXid; /* newest valid Xid in the SLRU */
TransactionId headXid; TransactionId tailXid; /* oldest xmin we might be interested in */
TransactionId tailXid;
bool warningIssued; bool warningIssued;
} OldSerXidControlData; } OldSerXidControlData;
@ -711,7 +710,6 @@ OldSerXidInit(void)
* Set control information to reflect empty SLRU. * Set control information to reflect empty SLRU.
*/ */
oldSerXidControl->headPage = -1; oldSerXidControl->headPage = -1;
oldSerXidControl->tailSegment = -1;
oldSerXidControl->headXid = InvalidTransactionId; oldSerXidControl->headXid = InvalidTransactionId;
oldSerXidControl->tailXid = InvalidTransactionId; oldSerXidControl->tailXid = InvalidTransactionId;
oldSerXidControl->warningIssued = false; oldSerXidControl->warningIssued = false;
@ -722,10 +720,6 @@ OldSerXidInit(void)
* Record a committed read write serializable xid and the minimum * Record a committed read write serializable xid and the minimum
* commitSeqNo of any transactions to which this xid had a rw-conflict out. * commitSeqNo of any transactions to which this xid had a rw-conflict out.
* A zero seqNo means that there were no conflicts out from xid. * A zero seqNo means that there were no conflicts out from xid.
*
* The return value is normally false -- true means that we're about to
* wrap around our space for tracking these xids, so the caller might want
* to take action to prevent that.
*/ */
static void static void
OldSerXidAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo) OldSerXidAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
@ -733,7 +727,7 @@ OldSerXidAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
TransactionId tailXid; TransactionId tailXid;
int targetPage; int targetPage;
int slotno; int slotno;
int page; int firstZeroPage;
int xidSpread; int xidSpread;
bool isNewPage; bool isNewPage;
@ -745,30 +739,34 @@ OldSerXidAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
/* /*
* If no serializable transactions are active, there shouldn't be anything * If no serializable transactions are active, there shouldn't be anything
* to push out to this SLRU. Hitting this assert would mean there's * to push out to the SLRU. Hitting this assert would mean there's
* something wrong with the earlier cleanup logic. * something wrong with the earlier cleanup logic.
*/ */
tailXid = oldSerXidControl->tailXid; tailXid = oldSerXidControl->tailXid;
Assert(TransactionIdIsValid(tailXid)); Assert(TransactionIdIsValid(tailXid));
/*
* If the SLRU is currently unused, zero out the whole active region
* from tailXid to headXid before taking it into use. Otherwise zero
* out only any new pages that enter the tailXid-headXid range as we
* advance headXid.
*/
if (oldSerXidControl->headPage < 0) if (oldSerXidControl->headPage < 0)
{ {
page = OldSerXidPage(tailXid); firstZeroPage = OldSerXidPage(tailXid);
oldSerXidControl->tailSegment = OldSerXidSegment(page);
page = oldSerXidControl->tailSegment * OLDSERXID_ENTRIESPERPAGE;
isNewPage = true; isNewPage = true;
} }
else else
{ {
page = OldSerXidNextPage(oldSerXidControl->headPage); firstZeroPage = OldSerXidNextPage(oldSerXidControl->headPage);
isNewPage = OldSerXidPagePrecedesLogically(oldSerXidControl->headPage, targetPage); isNewPage = OldSerXidPagePrecedesLogically(oldSerXidControl->headPage,
targetPage);
} }
if (!TransactionIdIsValid(oldSerXidControl->headXid) if (!TransactionIdIsValid(oldSerXidControl->headXid)
|| TransactionIdFollows(xid, oldSerXidControl->headXid)) || TransactionIdFollows(xid, oldSerXidControl->headXid))
oldSerXidControl->headXid = xid; oldSerXidControl->headXid = xid;
if (oldSerXidControl->headPage < 0 if (isNewPage)
|| OldSerXidPagePrecedesLogically(oldSerXidControl->headPage, targetPage))
oldSerXidControl->headPage = targetPage; oldSerXidControl->headPage = targetPage;
xidSpread = (((uint32) xid) - ((uint32) tailXid)); xidSpread = (((uint32) xid) - ((uint32) tailXid));
@ -788,10 +786,10 @@ OldSerXidAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
if (isNewPage) if (isNewPage)
{ {
/* Initialize intervening pages. */ /* Initialize intervening pages. */
while (page != targetPage) while (firstZeroPage != targetPage)
{ {
(void) SimpleLruZeroPage(OldSerXidSlruCtl, page); (void) SimpleLruZeroPage(OldSerXidSlruCtl, firstZeroPage);
page = OldSerXidNextPage(page); firstZeroPage = OldSerXidNextPage(firstZeroPage);
} }
slotno = SimpleLruZeroPage(OldSerXidSlruCtl, targetPage); slotno = SimpleLruZeroPage(OldSerXidSlruCtl, targetPage);
} }
@ -846,31 +844,24 @@ OldSerXidGetMinConflictCommitSeqNo(TransactionId xid)
/* /*
* Call this whenever there is a new xmin for active serializable * Call this whenever there is a new xmin for active serializable
* transactions. We don't need to keep information on transactions which * transactions. We don't need to keep information on transactions which
* preceed that. InvalidTransactionId means none active, so everything in * precede that. InvalidTransactionId means none active, so everything in
* the SLRU should be discarded. * the SLRU can be discarded.
*/ */
static void static void
OldSerXidSetActiveSerXmin(TransactionId xid) OldSerXidSetActiveSerXmin(TransactionId xid)
{ {
int newTailPage;
int newTailSegment;
LWLockAcquire(OldSerXidLock, LW_EXCLUSIVE); LWLockAcquire(OldSerXidLock, LW_EXCLUSIVE);
/* /*
* When no sxacts are active, nothing overlaps, set the xid values to * When no sxacts are active, nothing overlaps, set the xid values to
* invalid to show that there are no valid entries. Don't clear the * invalid to show that there are no valid entries. Don't clear headPage,
* segment/page information, though. A new xmin might still land in an * though. A new xmin might still land on that page, and we don't want
* existing segment, and we don't want to repeatedly delete and re-create * to repeatedly zero out the same page.
* the same segment file.
*/ */
if (!TransactionIdIsValid(xid)) if (!TransactionIdIsValid(xid))
{ {
if (TransactionIdIsValid(oldSerXidControl->tailXid))
{
oldSerXidControl->headXid = InvalidTransactionId;
oldSerXidControl->tailXid = InvalidTransactionId; oldSerXidControl->tailXid = InvalidTransactionId;
} oldSerXidControl->headXid = InvalidTransactionId;
LWLockRelease(OldSerXidLock); LWLockRelease(OldSerXidLock);
return; return;
} }
@ -886,7 +877,9 @@ OldSerXidSetActiveSerXmin(TransactionId xid)
Assert(oldSerXidControl->headPage < 0); Assert(oldSerXidControl->headPage < 0);
if (!TransactionIdIsValid(oldSerXidControl->tailXid) if (!TransactionIdIsValid(oldSerXidControl->tailXid)
|| TransactionIdPrecedes(xid, oldSerXidControl->tailXid)) || TransactionIdPrecedes(xid, oldSerXidControl->tailXid))
{
oldSerXidControl->tailXid = xid; oldSerXidControl->tailXid = xid;
}
LWLockRelease(OldSerXidLock); LWLockRelease(OldSerXidLock);
return; return;
} }
@ -896,37 +889,57 @@ OldSerXidSetActiveSerXmin(TransactionId xid)
oldSerXidControl->tailXid = xid; oldSerXidControl->tailXid = xid;
/* Exit quickly if there are no segments active. */ LWLockRelease(OldSerXidLock);
}
/*
* Perform a checkpoint --- either during shutdown, or on-the-fly
*
* We don't have any data that needs to survive a restart, but this is a
* convenient place to truncate the SLRU.
*/
void
CheckPointPredicate(void)
{
int tailPage;
LWLockAcquire(OldSerXidLock, LW_EXCLUSIVE);
/* Exit quickly if the SLRU is currently not in use. */
if (oldSerXidControl->headPage < 0) if (oldSerXidControl->headPage < 0)
{ {
LWLockRelease(OldSerXidLock); LWLockRelease(OldSerXidLock);
return; return;
} }
newTailPage = OldSerXidPage(xid); if (TransactionIdIsValid(oldSerXidControl->tailXid))
newTailSegment = OldSerXidSegment(newTailPage);
/* Exit quickly if we're still on the same segment. */
if (newTailSegment == oldSerXidControl->tailSegment)
{ {
LWLockRelease(OldSerXidLock); /* We can truncate the SLRU up to the page containing tailXid */
return; tailPage = OldSerXidPage(oldSerXidControl->tailXid);
} }
else
oldSerXidControl->tailSegment = newTailSegment;
/* See if that has cleared the last segment. */
if (OldSerXidPagePrecedesLogically(oldSerXidControl->headPage,
newTailSegment * SLRU_PAGES_PER_SEGMENT))
{ {
oldSerXidControl->headXid = InvalidTransactionId; /*
* The SLRU is no longer needed. Truncate everything but the last
* page. We don't dare to touch the last page in case the SLRU is
* taken back to use, and the new tail falls on the same page.
*/
tailPage = oldSerXidControl->headPage;
oldSerXidControl->headPage = -1; oldSerXidControl->headPage = -1;
oldSerXidControl->tailSegment = -1;
} }
LWLockRelease(OldSerXidLock); LWLockRelease(OldSerXidLock);
SimpleLruTruncate(OldSerXidSlruCtl, newTailPage); /*
* Flush dirty SLRU pages to disk
*
* This is not actually necessary from a correctness point of view. We do
* it merely as a debugging aid.
*/
SimpleLruFlush(OldSerXidSlruCtl, true);
/* Truncate away pages that are no longer required */
SimpleLruTruncate(OldSerXidSlruCtl, tailPage);
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/

@ -36,6 +36,8 @@ extern int max_predicate_locks_per_xact;
extern void InitPredicateLocks(void); extern void InitPredicateLocks(void);
extern Size PredicateLockShmemSize(void); extern Size PredicateLockShmemSize(void);
extern void CheckPointPredicate(void);
/* predicate lock reporting */ /* predicate lock reporting */
extern bool PageIsPredicateLocked(const Relation relation, const BlockNumber blkno); extern bool PageIsPredicateLocked(const Relation relation, const BlockNumber blkno);