mirror of
https://github.com/postgres/postgres.git
synced 2025-08-19 23:22:23 +03:00
Standard pgindent run for 8.1.
This commit is contained in:
@@ -4,15 +4,15 @@
|
||||
* PostgreSQL multi-transaction-log manager
|
||||
*
|
||||
* The pg_multixact manager is a pg_clog-like manager that stores an array
|
||||
* of TransactionIds for each MultiXactId. It is a fundamental part of the
|
||||
* shared-row-lock implementation. A share-locked tuple stores a
|
||||
* of TransactionIds for each MultiXactId. It is a fundamental part of the
|
||||
* shared-row-lock implementation. A share-locked tuple stores a
|
||||
* MultiXactId in its Xmax, and a transaction that needs to wait for the
|
||||
* tuple to be unlocked can sleep on the potentially-several TransactionIds
|
||||
* that compose the MultiXactId.
|
||||
*
|
||||
* We use two SLRU areas, one for storing the offsets at which the data
|
||||
* starts for each MultiXactId in the other one. This trick allows us to
|
||||
* store variable length arrays of TransactionIds. (We could alternatively
|
||||
* store variable length arrays of TransactionIds. (We could alternatively
|
||||
* use one area containing counts and TransactionIds, with valid MultiXactId
|
||||
* values pointing at slots containing counts; but that way seems less robust
|
||||
* since it would get completely confused if someone inquired about a bogus
|
||||
@@ -32,7 +32,7 @@
|
||||
*
|
||||
* Like clog.c, and unlike subtrans.c, we have to preserve state across
|
||||
* crashes and ensure that MXID and offset numbering increases monotonically
|
||||
* across a crash. We do this in the same way as it's done for transaction
|
||||
* across a crash. We do this in the same way as it's done for transaction
|
||||
* IDs: the WAL record is guaranteed to contain evidence of every MXID we
|
||||
* could need to worry about, and we just make sure that at the end of
|
||||
* replay, the next-MXID and next-offset counters are at least as large as
|
||||
@@ -42,7 +42,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.8 2005/08/20 23:26:08 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.9 2005/10/15 02:49:09 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -59,13 +59,13 @@
|
||||
|
||||
|
||||
/*
|
||||
* Defines for MultiXactOffset page sizes. A page is the same BLCKSZ as is
|
||||
* Defines for MultiXactOffset page sizes. A page is the same BLCKSZ as is
|
||||
* used everywhere else in Postgres.
|
||||
*
|
||||
* Note: because both MultiXactOffsets and TransactionIds are 32 bits and
|
||||
* wrap around at 0xFFFFFFFF, MultiXact page numbering also wraps around at
|
||||
* 0xFFFFFFFF/MULTIXACT_*_PER_PAGE, and segment numbering at
|
||||
* 0xFFFFFFFF/MULTIXACT_*_PER_PAGE/SLRU_SEGMENTS_PER_PAGE. We need take no
|
||||
* 0xFFFFFFFF/MULTIXACT_*_PER_PAGE/SLRU_SEGMENTS_PER_PAGE. We need take no
|
||||
* explicit notice of that fact in this module, except when comparing segment
|
||||
* and page numbers in TruncateMultiXact
|
||||
* (see MultiXact{Offset,Member}PagePrecedes).
|
||||
@@ -92,11 +92,11 @@
|
||||
static SlruCtlData MultiXactOffsetCtlData;
|
||||
static SlruCtlData MultiXactMemberCtlData;
|
||||
|
||||
#define MultiXactOffsetCtl (&MultiXactOffsetCtlData)
|
||||
#define MultiXactMemberCtl (&MultiXactMemberCtlData)
|
||||
#define MultiXactOffsetCtl (&MultiXactOffsetCtlData)
|
||||
#define MultiXactMemberCtl (&MultiXactMemberCtlData)
|
||||
|
||||
/*
|
||||
* MultiXact state shared across all backends. All this state is protected
|
||||
* MultiXact state shared across all backends. All this state is protected
|
||||
* by MultiXactGenLock. (We also use MultiXactOffsetControlLock and
|
||||
* MultiXactMemberControlLock to guard accesses to the two sets of SLRU
|
||||
* buffers. For concurrency's sake, we avoid holding more than one of these
|
||||
@@ -105,50 +105,48 @@ static SlruCtlData MultiXactMemberCtlData;
|
||||
typedef struct MultiXactStateData
|
||||
{
|
||||
/* next-to-be-assigned MultiXactId */
|
||||
MultiXactId nextMXact;
|
||||
MultiXactId nextMXact;
|
||||
|
||||
/* next-to-be-assigned offset */
|
||||
MultiXactOffset nextOffset;
|
||||
MultiXactOffset nextOffset;
|
||||
|
||||
/* the Offset SLRU area was last truncated at this MultiXactId */
|
||||
MultiXactId lastTruncationPoint;
|
||||
MultiXactId lastTruncationPoint;
|
||||
|
||||
/*
|
||||
* Per-backend data starts here. We have two arrays stored in
|
||||
* the area immediately following the MultiXactStateData struct.
|
||||
* Each is indexed by BackendId. (Note: valid BackendIds run from 1 to
|
||||
* MaxBackends; element zero of each array is never used.)
|
||||
* Per-backend data starts here. We have two arrays stored in the area
|
||||
* immediately following the MultiXactStateData struct. Each is indexed by
|
||||
* BackendId. (Note: valid BackendIds run from 1 to MaxBackends; element
|
||||
* zero of each array is never used.)
|
||||
*
|
||||
* OldestMemberMXactId[k] is the oldest MultiXactId each backend's
|
||||
* current transaction(s) could possibly be a member of, or
|
||||
* InvalidMultiXactId when the backend has no live transaction that
|
||||
* could possibly be a member of a MultiXact. Each backend sets its
|
||||
* entry to the current nextMXact counter just before first acquiring a
|
||||
* shared lock in a given transaction, and clears it at transaction end.
|
||||
* (This works because only during or after acquiring a shared lock
|
||||
* could an XID possibly become a member of a MultiXact, and that
|
||||
* MultiXact would have to be created during or after the lock
|
||||
* acquisition.)
|
||||
* OldestMemberMXactId[k] is the oldest MultiXactId each backend's current
|
||||
* transaction(s) could possibly be a member of, or InvalidMultiXactId
|
||||
* when the backend has no live transaction that could possibly be a
|
||||
* member of a MultiXact. Each backend sets its entry to the current
|
||||
* nextMXact counter just before first acquiring a shared lock in a given
|
||||
* transaction, and clears it at transaction end. (This works because only
|
||||
* during or after acquiring a shared lock could an XID possibly become a
|
||||
* member of a MultiXact, and that MultiXact would have to be created
|
||||
* during or after the lock acquisition.)
|
||||
*
|
||||
* OldestVisibleMXactId[k] is the oldest MultiXactId each backend's
|
||||
* current transaction(s) think is potentially live, or InvalidMultiXactId
|
||||
* when not in a transaction or not in a transaction that's paid any
|
||||
* attention to MultiXacts yet. This is computed when first needed in
|
||||
* a given transaction, and cleared at transaction end. We can compute
|
||||
* it as the minimum of the valid OldestMemberMXactId[] entries at the
|
||||
* time we compute it (using nextMXact if none are valid). Each backend
|
||||
* is required not to attempt to access any SLRU data for MultiXactIds
|
||||
* older than its own OldestVisibleMXactId[] setting; this is necessary
|
||||
* because the checkpointer could truncate away such data at any instant.
|
||||
* OldestVisibleMXactId[k] is the oldest MultiXactId each backend's current
|
||||
* transaction(s) think is potentially live, or InvalidMultiXactId when
|
||||
* not in a transaction or not in a transaction that's paid any attention
|
||||
* to MultiXacts yet. This is computed when first needed in a given
|
||||
* transaction, and cleared at transaction end. We can compute it as the
|
||||
* minimum of the valid OldestMemberMXactId[] entries at the time we
|
||||
* compute it (using nextMXact if none are valid). Each backend is
|
||||
* required not to attempt to access any SLRU data for MultiXactIds older
|
||||
* than its own OldestVisibleMXactId[] setting; this is necessary because
|
||||
* the checkpointer could truncate away such data at any instant.
|
||||
*
|
||||
* The checkpointer can compute the safe truncation point as the oldest
|
||||
* valid value among all the OldestMemberMXactId[] and
|
||||
* OldestVisibleMXactId[] entries, or nextMXact if none are valid.
|
||||
* Clearly, it is not possible for any later-computed OldestVisibleMXactId
|
||||
* value to be older than this, and so there is no risk of truncating
|
||||
* data that is still needed.
|
||||
* The checkpointer can compute the safe truncation point as the oldest valid
|
||||
* value among all the OldestMemberMXactId[] and OldestVisibleMXactId[]
|
||||
* entries, or nextMXact if none are valid. Clearly, it is not possible
|
||||
* for any later-computed OldestVisibleMXactId value to be older than
|
||||
* this, and so there is no risk of truncating data that is still needed.
|
||||
*/
|
||||
MultiXactId perBackendXactIds[1]; /* VARIABLE LENGTH ARRAY */
|
||||
MultiXactId perBackendXactIds[1]; /* VARIABLE LENGTH ARRAY */
|
||||
} MultiXactStateData;
|
||||
|
||||
/* Pointers to the state data in shared memory */
|
||||
@@ -176,13 +174,13 @@ static MultiXactId *OldestVisibleMXactId;
|
||||
typedef struct mXactCacheEnt
|
||||
{
|
||||
struct mXactCacheEnt *next;
|
||||
MultiXactId multi;
|
||||
int nxids;
|
||||
TransactionId xids[1]; /* VARIABLE LENGTH ARRAY */
|
||||
MultiXactId multi;
|
||||
int nxids;
|
||||
TransactionId xids[1]; /* VARIABLE LENGTH ARRAY */
|
||||
} mXactCacheEnt;
|
||||
|
||||
static mXactCacheEnt *MXactCache = NULL;
|
||||
static MemoryContext MXactContext = NULL;
|
||||
static mXactCacheEnt *MXactCache = NULL;
|
||||
static MemoryContext MXactContext = NULL;
|
||||
|
||||
|
||||
#ifdef MULTIXACT_DEBUG
|
||||
@@ -201,14 +199,15 @@ static MemoryContext MXactContext = NULL;
|
||||
static void MultiXactIdSetOldestVisible(void);
|
||||
static MultiXactId CreateMultiXactId(int nxids, TransactionId *xids);
|
||||
static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
|
||||
int nxids, TransactionId *xids);
|
||||
int nxids, TransactionId *xids);
|
||||
static MultiXactId GetNewMultiXactId(int nxids, MultiXactOffset *offset);
|
||||
|
||||
/* MultiXact cache management */
|
||||
static MultiXactId mXactCacheGetBySet(int nxids, TransactionId *xids);
|
||||
static int mXactCacheGetById(MultiXactId multi, TransactionId **xids);
|
||||
static int mXactCacheGetById(MultiXactId multi, TransactionId **xids);
|
||||
static void mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids);
|
||||
static int xidComparator(const void *arg1, const void *arg2);
|
||||
static int xidComparator(const void *arg1, const void *arg2);
|
||||
|
||||
#ifdef MULTIXACT_DEBUG
|
||||
static char *mxid_to_string(MultiXactId multi, int nxids, TransactionId *xids);
|
||||
#endif
|
||||
@@ -220,7 +219,7 @@ static bool MultiXactOffsetPagePrecedes(int page1, int page2);
|
||||
static bool MultiXactMemberPagePrecedes(int page1, int page2);
|
||||
static bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2);
|
||||
static bool MultiXactOffsetPrecedes(MultiXactOffset offset1,
|
||||
MultiXactOffset offset2);
|
||||
MultiXactOffset offset2);
|
||||
static void ExtendMultiXactOffset(MultiXactId multi);
|
||||
static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers);
|
||||
static void TruncateMultiXact(void);
|
||||
@@ -239,8 +238,8 @@ static void WriteMZeroPageXlogRec(int pageno, uint8 info);
|
||||
MultiXactId
|
||||
MultiXactIdCreate(TransactionId xid1, TransactionId xid2)
|
||||
{
|
||||
MultiXactId newMulti;
|
||||
TransactionId xids[2];
|
||||
MultiXactId newMulti;
|
||||
TransactionId xids[2];
|
||||
|
||||
AssertArg(TransactionIdIsValid(xid1));
|
||||
AssertArg(TransactionIdIsValid(xid2));
|
||||
@@ -248,9 +247,9 @@ MultiXactIdCreate(TransactionId xid1, TransactionId xid2)
|
||||
Assert(!TransactionIdEquals(xid1, xid2));
|
||||
|
||||
/*
|
||||
* Note: unlike MultiXactIdExpand, we don't bother to check that both
|
||||
* XIDs are still running. In typical usage, xid2 will be our own XID
|
||||
* and the caller just did a check on xid1, so it'd be wasted effort.
|
||||
* Note: unlike MultiXactIdExpand, we don't bother to check that both XIDs
|
||||
* are still running. In typical usage, xid2 will be our own XID and the
|
||||
* caller just did a check on xid1, so it'd be wasted effort.
|
||||
*/
|
||||
|
||||
xids[0] = xid1;
|
||||
@@ -281,12 +280,12 @@ MultiXactIdCreate(TransactionId xid1, TransactionId xid2)
|
||||
MultiXactId
|
||||
MultiXactIdExpand(MultiXactId multi, TransactionId xid)
|
||||
{
|
||||
MultiXactId newMulti;
|
||||
TransactionId *members;
|
||||
TransactionId *newMembers;
|
||||
int nmembers;
|
||||
int i;
|
||||
int j;
|
||||
MultiXactId newMulti;
|
||||
TransactionId *members;
|
||||
TransactionId *newMembers;
|
||||
int nmembers;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
AssertArg(MultiXactIdIsValid(multi));
|
||||
AssertArg(TransactionIdIsValid(xid));
|
||||
@@ -313,8 +312,8 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
|
||||
}
|
||||
|
||||
/*
|
||||
* If the TransactionId is already a member of the MultiXactId,
|
||||
* just return the existing MultiXactId.
|
||||
* If the TransactionId is already a member of the MultiXactId, just
|
||||
* return the existing MultiXactId.
|
||||
*/
|
||||
for (i = 0; i < nmembers; i++)
|
||||
{
|
||||
@@ -329,9 +328,9 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
|
||||
|
||||
/*
|
||||
* Determine which of the members of the MultiXactId are still running,
|
||||
* and use them to create a new one. (Removing dead members is just
|
||||
* an optimization, but a useful one. Note we have the same race
|
||||
* condition here as above: j could be 0 at the end of the loop.)
|
||||
* and use them to create a new one. (Removing dead members is just an
|
||||
* optimization, but a useful one. Note we have the same race condition
|
||||
* here as above: j could be 0 at the end of the loop.)
|
||||
*/
|
||||
newMembers = (TransactionId *)
|
||||
palloc(sizeof(TransactionId) * (nmembers + 1));
|
||||
@@ -355,7 +354,7 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
|
||||
|
||||
/*
|
||||
* MultiXactIdIsRunning
|
||||
* Returns whether a MultiXactId is "running".
|
||||
* Returns whether a MultiXactId is "running".
|
||||
*
|
||||
* We return true if at least one member of the given MultiXactId is still
|
||||
* running. Note that a "false" result is certain not to change,
|
||||
@@ -365,9 +364,9 @@ bool
|
||||
MultiXactIdIsRunning(MultiXactId multi)
|
||||
{
|
||||
TransactionId *members;
|
||||
TransactionId myXid;
|
||||
int nmembers;
|
||||
int i;
|
||||
TransactionId myXid;
|
||||
int nmembers;
|
||||
int i;
|
||||
|
||||
debug_elog3(DEBUG2, "IsRunning %u?", multi);
|
||||
|
||||
@@ -394,7 +393,7 @@ MultiXactIdIsRunning(MultiXactId multi)
|
||||
|
||||
/*
|
||||
* This could be made faster by having another entry point in procarray.c,
|
||||
* walking the PGPROC array only once for all the members. But in most
|
||||
* walking the PGPROC array only once for all the members. But in most
|
||||
* cases nmembers should be small enough that it doesn't much matter.
|
||||
*/
|
||||
for (i = 0; i < nmembers; i++)
|
||||
@@ -436,19 +435,19 @@ MultiXactIdSetOldestMember(void)
|
||||
|
||||
/*
|
||||
* You might think we don't need to acquire a lock here, since
|
||||
* fetching and storing of TransactionIds is probably atomic,
|
||||
* but in fact we do: suppose we pick up nextMXact and then
|
||||
* lose the CPU for a long time. Someone else could advance
|
||||
* nextMXact, and then another someone else could compute an
|
||||
* OldestVisibleMXactId that would be after the value we are
|
||||
* going to store when we get control back. Which would be wrong.
|
||||
* fetching and storing of TransactionIds is probably atomic, but in
|
||||
* fact we do: suppose we pick up nextMXact and then lose the CPU for
|
||||
* a long time. Someone else could advance nextMXact, and then
|
||||
* another someone else could compute an OldestVisibleMXactId that
|
||||
* would be after the value we are going to store when we get control
|
||||
* back. Which would be wrong.
|
||||
*/
|
||||
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
|
||||
|
||||
/*
|
||||
* We have to beware of the possibility that nextMXact is in the
|
||||
* wrapped-around state. We don't fix the counter itself here,
|
||||
* but we must be sure to store a valid value in our array entry.
|
||||
* wrapped-around state. We don't fix the counter itself here, but we
|
||||
* must be sure to store a valid value in our array entry.
|
||||
*/
|
||||
nextMXact = MultiXactState->nextMXact;
|
||||
if (nextMXact < FirstMultiXactId)
|
||||
@@ -475,7 +474,7 @@ MultiXactIdSetOldestMember(void)
|
||||
* The value to set is the oldest of nextMXact and all the valid per-backend
|
||||
* OldestMemberMXactId[] entries. Because of the locking we do, we can be
|
||||
* certain that no subsequent call to MultiXactIdSetOldestMember can set
|
||||
* an OldestMemberMXactId[] entry older than what we compute here. Therefore
|
||||
* an OldestMemberMXactId[] entry older than what we compute here. Therefore
|
||||
* there is no live transaction, now or later, that can be a member of any
|
||||
* MultiXactId older than the OldestVisibleMXactId we compute here.
|
||||
*/
|
||||
@@ -485,14 +484,14 @@ MultiXactIdSetOldestVisible(void)
|
||||
if (!MultiXactIdIsValid(OldestVisibleMXactId[MyBackendId]))
|
||||
{
|
||||
MultiXactId oldestMXact;
|
||||
int i;
|
||||
int i;
|
||||
|
||||
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
|
||||
|
||||
/*
|
||||
* We have to beware of the possibility that nextMXact is in the
|
||||
* wrapped-around state. We don't fix the counter itself here,
|
||||
* but we must be sure to store a valid value in our array entry.
|
||||
* wrapped-around state. We don't fix the counter itself here, but we
|
||||
* must be sure to store a valid value in our array entry.
|
||||
*/
|
||||
oldestMXact = MultiXactState->nextMXact;
|
||||
if (oldestMXact < FirstMultiXactId)
|
||||
@@ -535,17 +534,17 @@ void
|
||||
MultiXactIdWait(MultiXactId multi)
|
||||
{
|
||||
TransactionId *members;
|
||||
int nmembers;
|
||||
int nmembers;
|
||||
|
||||
nmembers = GetMultiXactIdMembers(multi, &members);
|
||||
|
||||
if (nmembers >= 0)
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nmembers; i++)
|
||||
{
|
||||
TransactionId member = members[i];
|
||||
TransactionId member = members[i];
|
||||
|
||||
debug_elog4(DEBUG2, "MultiXactIdWait: waiting for %d (%u)",
|
||||
i, member);
|
||||
@@ -564,19 +563,19 @@ MultiXactIdWait(MultiXactId multi)
|
||||
bool
|
||||
ConditionalMultiXactIdWait(MultiXactId multi)
|
||||
{
|
||||
bool result = true;
|
||||
bool result = true;
|
||||
TransactionId *members;
|
||||
int nmembers;
|
||||
int nmembers;
|
||||
|
||||
nmembers = GetMultiXactIdMembers(multi, &members);
|
||||
|
||||
if (nmembers >= 0)
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nmembers; i++)
|
||||
{
|
||||
TransactionId member = members[i];
|
||||
TransactionId member = members[i];
|
||||
|
||||
debug_elog4(DEBUG2, "ConditionalMultiXactIdWait: trying %d (%u)",
|
||||
i, member);
|
||||
@@ -596,7 +595,7 @@ ConditionalMultiXactIdWait(MultiXactId multi)
|
||||
|
||||
/*
|
||||
* CreateMultiXactId
|
||||
* Make a new MultiXactId
|
||||
* Make a new MultiXactId
|
||||
*
|
||||
* Make XLOG, SLRU and cache entries for a new MultiXactId, recording the
|
||||
* given TransactionIds as members. Returns the newly created MultiXactId.
|
||||
@@ -606,7 +605,7 @@ ConditionalMultiXactIdWait(MultiXactId multi)
|
||||
static MultiXactId
|
||||
CreateMultiXactId(int nxids, TransactionId *xids)
|
||||
{
|
||||
MultiXactId multi;
|
||||
MultiXactId multi;
|
||||
MultiXactOffset offset;
|
||||
XLogRecData rdata[2];
|
||||
xl_multixact_create xlrec;
|
||||
@@ -641,15 +640,15 @@ CreateMultiXactId(int nxids, TransactionId *xids)
|
||||
/*
|
||||
* Make an XLOG entry describing the new MXID.
|
||||
*
|
||||
* Note: we need not flush this XLOG entry to disk before proceeding.
|
||||
* The only way for the MXID to be referenced from any data page is
|
||||
* for heap_lock_tuple() to have put it there, and heap_lock_tuple()
|
||||
* generates an XLOG record that must follow ours. The normal LSN
|
||||
* interlock between the data page and that XLOG record will ensure
|
||||
* that our XLOG record reaches disk first. If the SLRU members/offsets
|
||||
* data reaches disk sooner than the XLOG record, we do not care because
|
||||
* we'll overwrite it with zeroes unless the XLOG record is there too;
|
||||
* see notes at top of this file.
|
||||
* Note: we need not flush this XLOG entry to disk before proceeding. The
|
||||
* only way for the MXID to be referenced from any data page is for
|
||||
* heap_lock_tuple() to have put it there, and heap_lock_tuple() generates
|
||||
* an XLOG record that must follow ours. The normal LSN interlock between
|
||||
* the data page and that XLOG record will ensure that our XLOG record
|
||||
* reaches disk first. If the SLRU members/offsets data reaches disk
|
||||
* sooner than the XLOG record, we do not care because we'll overwrite it
|
||||
* with zeroes unless the XLOG record is there too; see notes at top of
|
||||
* this file.
|
||||
*/
|
||||
xlrec.mid = multi;
|
||||
xlrec.moff = offset;
|
||||
@@ -702,9 +701,9 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
|
||||
/*
|
||||
* Note: we pass the MultiXactId to SimpleLruReadPage as the "transaction"
|
||||
* to complain about if there's any I/O error. This is kinda bogus, but
|
||||
* since the errors will always give the full pathname, it should be
|
||||
* clear enough that a MultiXactId is really involved. Perhaps someday
|
||||
* we'll take the trouble to generalize the slru.c error reporting code.
|
||||
* since the errors will always give the full pathname, it should be clear
|
||||
* enough that a MultiXactId is really involved. Perhaps someday we'll
|
||||
* take the trouble to generalize the slru.c error reporting code.
|
||||
*/
|
||||
slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, multi);
|
||||
offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
|
||||
@@ -750,7 +749,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
|
||||
* GetNewMultiXactId
|
||||
* Get the next MultiXactId.
|
||||
*
|
||||
* Also, reserve the needed amount of space in the "members" area. The
|
||||
* Also, reserve the needed amount of space in the "members" area. The
|
||||
* starting offset of the reserved space is returned in *offset.
|
||||
*
|
||||
* This may generate XLOG records for expansion of the offsets and/or members
|
||||
@@ -761,7 +760,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
|
||||
static MultiXactId
|
||||
GetNewMultiXactId(int nxids, MultiXactOffset *offset)
|
||||
{
|
||||
MultiXactId result;
|
||||
MultiXactId result;
|
||||
|
||||
debug_elog3(DEBUG2, "GetNew: for %d xids", nxids);
|
||||
|
||||
@@ -785,8 +784,8 @@ GetNewMultiXactId(int nxids, MultiXactOffset *offset)
|
||||
* Advance counter. As in GetNewTransactionId(), this must not happen
|
||||
* until after ExtendMultiXactOffset has succeeded!
|
||||
*
|
||||
* We don't care about MultiXactId wraparound here; it will be handled by
|
||||
* the next iteration. But note that nextMXact may be InvalidMultiXactId
|
||||
* We don't care about MultiXactId wraparound here; it will be handled by the
|
||||
* next iteration. But note that nextMXact may be InvalidMultiXactId
|
||||
* after this routine exits, so anyone else looking at the variable must
|
||||
* be prepared to deal with that.
|
||||
*/
|
||||
@@ -809,7 +808,7 @@ GetNewMultiXactId(int nxids, MultiXactOffset *offset)
|
||||
|
||||
/*
|
||||
* GetMultiXactIdMembers
|
||||
* Returns the set of TransactionIds that make up a MultiXactId
|
||||
* Returns the set of TransactionIds that make up a MultiXactId
|
||||
*
|
||||
* We return -1 if the MultiXactId is too old to possibly have any members
|
||||
* still running; in that case we have not actually looked them up, and
|
||||
@@ -822,13 +821,13 @@ GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
|
||||
int prev_pageno;
|
||||
int entryno;
|
||||
int slotno;
|
||||
MultiXactOffset *offptr;
|
||||
MultiXactOffset offset;
|
||||
MultiXactOffset *offptr;
|
||||
MultiXactOffset offset;
|
||||
int length;
|
||||
int i;
|
||||
MultiXactId nextMXact;
|
||||
MultiXactId tmpMXact;
|
||||
MultiXactOffset nextOffset;
|
||||
MultiXactId nextMXact;
|
||||
MultiXactId tmpMXact;
|
||||
MultiXactOffset nextOffset;
|
||||
TransactionId *ptr;
|
||||
|
||||
debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);
|
||||
@@ -850,13 +849,13 @@ GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
|
||||
/*
|
||||
* We check known limits on MultiXact before resorting to the SLRU area.
|
||||
*
|
||||
* An ID older than our OldestVisibleMXactId[] entry can't possibly still
|
||||
* be running, and we'd run the risk of trying to read already-truncated
|
||||
* SLRU data if we did try to examine it.
|
||||
* An ID older than our OldestVisibleMXactId[] entry can't possibly still be
|
||||
* running, and we'd run the risk of trying to read already-truncated SLRU
|
||||
* data if we did try to examine it.
|
||||
*
|
||||
* Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is
|
||||
* seen, it implies undetected ID wraparound has occurred. We just
|
||||
* silently assume that such an ID is no longer running.
|
||||
* Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is seen,
|
||||
* it implies undetected ID wraparound has occurred. We just silently
|
||||
* assume that such an ID is no longer running.
|
||||
*
|
||||
* Shared lock is enough here since we aren't modifying any global state.
|
||||
* Also, we can examine our own OldestVisibleMXactId without the lock,
|
||||
@@ -880,9 +879,9 @@ GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
|
||||
}
|
||||
|
||||
/*
|
||||
* Before releasing the lock, save the current counter values, because
|
||||
* the target MultiXactId may be just one less than nextMXact. We will
|
||||
* need to use nextOffset as the endpoint if so.
|
||||
* Before releasing the lock, save the current counter values, because the
|
||||
* target MultiXactId may be just one less than nextMXact. We will need
|
||||
* to use nextOffset as the endpoint if so.
|
||||
*/
|
||||
nextMXact = MultiXactState->nextMXact;
|
||||
nextOffset = MultiXactState->nextOffset;
|
||||
@@ -902,11 +901,11 @@ GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
|
||||
|
||||
/*
|
||||
* How many members do we need to read? If we are at the end of the
|
||||
* assigned MultiXactIds, use the offset just saved above. Else we
|
||||
* need to check the MultiXactId following ours.
|
||||
* assigned MultiXactIds, use the offset just saved above. Else we need
|
||||
* to check the MultiXactId following ours.
|
||||
*
|
||||
* Use the same increment rule as GetNewMultiXactId(), that is, don't
|
||||
* handle wraparound explicitly until needed.
|
||||
* Use the same increment rule as GetNewMultiXactId(), that is, don't handle
|
||||
* wraparound explicitly until needed.
|
||||
*/
|
||||
tmpMXact = multi + 1;
|
||||
|
||||
@@ -974,9 +973,9 @@ GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
|
||||
|
||||
/*
|
||||
* mXactCacheGetBySet
|
||||
* returns a MultiXactId from the cache based on the set of
|
||||
* TransactionIds that compose it, or InvalidMultiXactId if
|
||||
* none matches.
|
||||
* returns a MultiXactId from the cache based on the set of
|
||||
* TransactionIds that compose it, or InvalidMultiXactId if
|
||||
* none matches.
|
||||
*
|
||||
* This is helpful, for example, if two transactions want to lock a huge
|
||||
* table. By using the cache, the second will use the same MultiXactId
|
||||
@@ -988,7 +987,7 @@ GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
|
||||
static MultiXactId
|
||||
mXactCacheGetBySet(int nxids, TransactionId *xids)
|
||||
{
|
||||
mXactCacheEnt *entry;
|
||||
mXactCacheEnt *entry;
|
||||
|
||||
debug_elog3(DEBUG2, "CacheGet: looking for %s",
|
||||
mxid_to_string(InvalidMultiXactId, nxids, xids));
|
||||
@@ -1015,8 +1014,8 @@ mXactCacheGetBySet(int nxids, TransactionId *xids)
|
||||
|
||||
/*
|
||||
* mXactCacheGetById
|
||||
* returns the composing TransactionId set from the cache for a
|
||||
* given MultiXactId, if present.
|
||||
* returns the composing TransactionId set from the cache for a
|
||||
* given MultiXactId, if present.
|
||||
*
|
||||
* If successful, *xids is set to the address of a palloc'd copy of the
|
||||
* TransactionId set. Return value is number of members, or -1 on failure.
|
||||
@@ -1024,7 +1023,7 @@ mXactCacheGetBySet(int nxids, TransactionId *xids)
|
||||
static int
|
||||
mXactCacheGetById(MultiXactId multi, TransactionId **xids)
|
||||
{
|
||||
mXactCacheEnt *entry;
|
||||
mXactCacheEnt *entry;
|
||||
|
||||
debug_elog3(DEBUG2, "CacheGet: looking for %u", multi);
|
||||
|
||||
@@ -1032,7 +1031,7 @@ mXactCacheGetById(MultiXactId multi, TransactionId **xids)
|
||||
{
|
||||
if (entry->multi == multi)
|
||||
{
|
||||
TransactionId *ptr;
|
||||
TransactionId *ptr;
|
||||
Size size;
|
||||
|
||||
size = sizeof(TransactionId) * entry->nxids;
|
||||
@@ -1042,7 +1041,7 @@ mXactCacheGetById(MultiXactId multi, TransactionId **xids)
|
||||
memcpy(ptr, entry->xids, size);
|
||||
|
||||
debug_elog3(DEBUG2, "CacheGet: found %s",
|
||||
mxid_to_string(multi, entry->nxids, entry->xids));
|
||||
mxid_to_string(multi, entry->nxids, entry->xids));
|
||||
return entry->nxids;
|
||||
}
|
||||
}
|
||||
@@ -1053,12 +1052,12 @@ mXactCacheGetById(MultiXactId multi, TransactionId **xids)
|
||||
|
||||
/*
|
||||
* mXactCachePut
|
||||
* Add a new MultiXactId and its composing set into the local cache.
|
||||
* Add a new MultiXactId and its composing set into the local cache.
|
||||
*/
|
||||
static void
|
||||
mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids)
|
||||
{
|
||||
mXactCacheEnt *entry;
|
||||
mXactCacheEnt *entry;
|
||||
|
||||
debug_elog3(DEBUG2, "CachePut: storing %s",
|
||||
mxid_to_string(multi, nxids, xids));
|
||||
@@ -1092,7 +1091,7 @@ mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids)
|
||||
|
||||
/*
|
||||
* xidComparator
|
||||
* qsort comparison function for XIDs
|
||||
* qsort comparison function for XIDs
|
||||
*
|
||||
* We don't need to use wraparound comparison for XIDs, and indeed must
|
||||
* not do so since that does not respect the triangle inequality! Any
|
||||
@@ -1101,8 +1100,8 @@ mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids)
|
||||
static int
|
||||
xidComparator(const void *arg1, const void *arg2)
|
||||
{
|
||||
TransactionId xid1 = * (const TransactionId *) arg1;
|
||||
TransactionId xid2 = * (const TransactionId *) arg2;
|
||||
TransactionId xid1 = *(const TransactionId *) arg1;
|
||||
TransactionId xid2 = *(const TransactionId *) arg2;
|
||||
|
||||
if (xid1 > xid2)
|
||||
return 1;
|
||||
@@ -1115,8 +1114,9 @@ xidComparator(const void *arg1, const void *arg2)
|
||||
static char *
|
||||
mxid_to_string(MultiXactId multi, int nxids, TransactionId *xids)
|
||||
{
|
||||
char *str = palloc(15 * (nxids + 1) + 4);
|
||||
int i;
|
||||
char *str = palloc(15 * (nxids + 1) + 4);
|
||||
int i;
|
||||
|
||||
snprintf(str, 47, "%u %d[%u", multi, nxids, xids[0]);
|
||||
|
||||
for (i = 1; i < nxids; i++)
|
||||
@@ -1137,18 +1137,18 @@ void
|
||||
AtEOXact_MultiXact(void)
|
||||
{
|
||||
/*
|
||||
* Reset our OldestMemberMXactId and OldestVisibleMXactId values,
|
||||
* both of which should only be valid while within a transaction.
|
||||
* Reset our OldestMemberMXactId and OldestVisibleMXactId values, both of
|
||||
* which should only be valid while within a transaction.
|
||||
*
|
||||
* We assume that storing a MultiXactId is atomic and so we need
|
||||
* not take MultiXactGenLock to do this.
|
||||
* We assume that storing a MultiXactId is atomic and so we need not take
|
||||
* MultiXactGenLock to do this.
|
||||
*/
|
||||
OldestMemberMXactId[MyBackendId] = InvalidMultiXactId;
|
||||
OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId;
|
||||
|
||||
/*
|
||||
* Discard the local MultiXactId cache. Since MXactContext was created
|
||||
* as a child of TopTransactionContext, we needn't delete it explicitly.
|
||||
* Discard the local MultiXactId cache. Since MXactContext was created as
|
||||
* a child of TopTransactionContext, we needn't delete it explicitly.
|
||||
*/
|
||||
MXactContext = NULL;
|
||||
MXactCache = NULL;
|
||||
@@ -1156,7 +1156,7 @@ AtEOXact_MultiXact(void)
|
||||
|
||||
/*
|
||||
* Initialization of shared memory for MultiXact. We use two SLRU areas,
|
||||
* thus double memory. Also, reserve space for the shared MultiXactState
|
||||
* thus double memory. Also, reserve space for the shared MultiXactState
|
||||
* struct and the per-backend MultiXactId arrays (two of those, too).
|
||||
*/
|
||||
Size
|
||||
@@ -1178,7 +1178,7 @@ MultiXactShmemSize(void)
|
||||
void
|
||||
MultiXactShmemInit(void)
|
||||
{
|
||||
bool found;
|
||||
bool found;
|
||||
|
||||
debug_elog2(DEBUG2, "Shared Memory Init for MultiXact");
|
||||
|
||||
@@ -1205,8 +1205,8 @@ MultiXactShmemInit(void)
|
||||
Assert(found);
|
||||
|
||||
/*
|
||||
* Set up array pointers. Note that perBackendXactIds[0] is wasted
|
||||
* space since we only use indexes 1..MaxBackends in each array.
|
||||
* Set up array pointers. Note that perBackendXactIds[0] is wasted space
|
||||
* since we only use indexes 1..MaxBackends in each array.
|
||||
*/
|
||||
OldestMemberMXactId = MultiXactState->perBackendXactIds;
|
||||
OldestVisibleMXactId = OldestMemberMXactId + MaxBackends;
|
||||
@@ -1214,7 +1214,7 @@ MultiXactShmemInit(void)
|
||||
|
||||
/*
|
||||
* This func must be called ONCE on system install. It creates the initial
|
||||
* MultiXact segments. (The MultiXacts directories are assumed to have been
|
||||
* MultiXact segments. (The MultiXacts directories are assumed to have been
|
||||
* created by initdb, and MultiXactShmemInit must have been called already.)
|
||||
*/
|
||||
void
|
||||
@@ -1287,7 +1287,7 @@ ZeroMultiXactMemberPage(int pageno, bool writeXlog)
|
||||
* This must be called ONCE during postmaster or standalone-backend startup.
|
||||
*
|
||||
* StartupXLOG has already established nextMXact/nextOffset by calling
|
||||
* MultiXactSetNextMXact and/or MultiXactAdvanceNextMXact. Note that we
|
||||
* MultiXactSetNextMXact and/or MultiXactAdvanceNextMXact. Note that we
|
||||
* may already have replayed WAL data into the SLRU files.
|
||||
*
|
||||
* We don't need any locks here, really; the SLRU locks are taken
|
||||
@@ -1311,14 +1311,14 @@ StartupMultiXact(void)
|
||||
MultiXactOffsetCtl->shared->latest_page_number = pageno;
|
||||
|
||||
/*
|
||||
* Zero out the remainder of the current offsets page. See notes
|
||||
* in StartupCLOG() for motivation.
|
||||
* Zero out the remainder of the current offsets page. See notes in
|
||||
* StartupCLOG() for motivation.
|
||||
*/
|
||||
entryno = MultiXactIdToOffsetEntry(multi);
|
||||
if (entryno != 0)
|
||||
{
|
||||
int slotno;
|
||||
MultiXactOffset *offptr;
|
||||
MultiXactOffset *offptr;
|
||||
|
||||
slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, multi);
|
||||
offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
|
||||
@@ -1341,14 +1341,14 @@ StartupMultiXact(void)
|
||||
MultiXactMemberCtl->shared->latest_page_number = pageno;
|
||||
|
||||
/*
|
||||
* Zero out the remainder of the current members page. See notes
|
||||
* in StartupCLOG() for motivation.
|
||||
* Zero out the remainder of the current members page. See notes in
|
||||
* StartupCLOG() for motivation.
|
||||
*/
|
||||
entryno = MXOffsetToMemberEntry(offset);
|
||||
if (entryno != 0)
|
||||
{
|
||||
int slotno;
|
||||
TransactionId *xidptr;
|
||||
TransactionId *xidptr;
|
||||
|
||||
slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, offset);
|
||||
xidptr = (TransactionId *) MultiXactMemberCtl->shared->page_buffer[slotno];
|
||||
@@ -1499,14 +1499,14 @@ static void
|
||||
ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
|
||||
{
|
||||
/*
|
||||
* It's possible that the members span more than one page of the
|
||||
* members file, so we loop to ensure we consider each page. The
|
||||
* coding is not optimal if the members span several pages, but
|
||||
* that seems unusual enough to not worry much about.
|
||||
* It's possible that the members span more than one page of the members
|
||||
* file, so we loop to ensure we consider each page. The coding is not
|
||||
* optimal if the members span several pages, but that seems unusual
|
||||
* enough to not worry much about.
|
||||
*/
|
||||
while (nmembers > 0)
|
||||
{
|
||||
int entryno;
|
||||
int entryno;
|
||||
|
||||
/*
|
||||
* Only zero when at first entry of a page.
|
||||
@@ -1514,7 +1514,7 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
|
||||
entryno = MXOffsetToMemberEntry(offset);
|
||||
if (entryno == 0)
|
||||
{
|
||||
int pageno;
|
||||
int pageno;
|
||||
|
||||
pageno = MXOffsetToMemberPage(offset);
|
||||
|
||||
@@ -1536,7 +1536,7 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
|
||||
* Remove all MultiXactOffset and MultiXactMember segments before the oldest
|
||||
* ones still of interest.
|
||||
*
|
||||
* This is called only during checkpoints. We assume no more than one
|
||||
* This is called only during checkpoints. We assume no more than one
|
||||
* backend does this at a time.
|
||||
*
|
||||
* XXX do we have any issues with needing to checkpoint here?
|
||||
@@ -1545,23 +1545,23 @@ static void
|
||||
TruncateMultiXact(void)
|
||||
{
|
||||
MultiXactId nextMXact;
|
||||
MultiXactOffset nextOffset;
|
||||
MultiXactOffset nextOffset;
|
||||
MultiXactId oldestMXact;
|
||||
MultiXactOffset oldestOffset;
|
||||
MultiXactOffset oldestOffset;
|
||||
int cutoffPage;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* First, compute where we can safely truncate. Per notes above,
|
||||
* this is the oldest valid value among all the OldestMemberMXactId[] and
|
||||
* First, compute where we can safely truncate. Per notes above, this is
|
||||
* the oldest valid value among all the OldestMemberMXactId[] and
|
||||
* OldestVisibleMXactId[] entries, or nextMXact if none are valid.
|
||||
*/
|
||||
LWLockAcquire(MultiXactGenLock, LW_SHARED);
|
||||
|
||||
/*
|
||||
* We have to beware of the possibility that nextMXact is in the
|
||||
* wrapped-around state. We don't fix the counter itself here,
|
||||
* but we must be sure to use a valid value in our calculation.
|
||||
* wrapped-around state. We don't fix the counter itself here, but we
|
||||
* must be sure to use a valid value in our calculation.
|
||||
*/
|
||||
nextMXact = MultiXactState->nextMXact;
|
||||
if (nextMXact < FirstMultiXactId)
|
||||
@@ -1597,9 +1597,9 @@ TruncateMultiXact(void)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We need to determine where to truncate MultiXactMember. If we
|
||||
* found a valid oldest MultiXactId, read its starting offset;
|
||||
* otherwise we use the nextOffset value we saved above.
|
||||
* We need to determine where to truncate MultiXactMember. If we found a
|
||||
* valid oldest MultiXactId, read its starting offset; otherwise we use
|
||||
* the nextOffset value we saved above.
|
||||
*/
|
||||
if (oldestMXact == nextMXact)
|
||||
oldestOffset = nextOffset;
|
||||
@@ -1608,7 +1608,7 @@ TruncateMultiXact(void)
|
||||
int pageno;
|
||||
int slotno;
|
||||
int entryno;
|
||||
MultiXactOffset *offptr;
|
||||
MultiXactOffset *offptr;
|
||||
|
||||
LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
|
||||
|
||||
@@ -1624,8 +1624,8 @@ TruncateMultiXact(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* The cutoff point is the start of the segment containing oldestMXact.
|
||||
* We pass the *page* containing oldestMXact to SimpleLruTruncate.
|
||||
* The cutoff point is the start of the segment containing oldestMXact. We
|
||||
* pass the *page* containing oldestMXact to SimpleLruTruncate.
|
||||
*/
|
||||
cutoffPage = MultiXactIdToOffsetPage(oldestMXact);
|
||||
|
||||
@@ -1677,8 +1677,8 @@ MultiXactOffsetPagePrecedes(int page1, int page2)
|
||||
static bool
|
||||
MultiXactMemberPagePrecedes(int page1, int page2)
|
||||
{
|
||||
MultiXactOffset offset1;
|
||||
MultiXactOffset offset2;
|
||||
MultiXactOffset offset1;
|
||||
MultiXactOffset offset2;
|
||||
|
||||
offset1 = ((MultiXactOffset) page1) * MULTIXACT_MEMBERS_PER_PAGE;
|
||||
offset2 = ((MultiXactOffset) page2) * MULTIXACT_MEMBERS_PER_PAGE;
|
||||
@@ -1695,7 +1695,7 @@ MultiXactMemberPagePrecedes(int page1, int page2)
|
||||
static bool
|
||||
MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
|
||||
{
|
||||
int32 diff = (int32) (multi1 - multi2);
|
||||
int32 diff = (int32) (multi1 - multi2);
|
||||
|
||||
return (diff < 0);
|
||||
}
|
||||
@@ -1706,7 +1706,7 @@ MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
|
||||
static bool
|
||||
MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
|
||||
{
|
||||
int32 diff = (int32) (offset1 - offset2);
|
||||
int32 diff = (int32) (offset1 - offset2);
|
||||
|
||||
return (diff < 0);
|
||||
}
|
||||
@@ -1783,9 +1783,9 @@ multixact_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||
MultiXactAdvanceNextMXact(xlrec->mid + 1, xlrec->moff + xlrec->nxids);
|
||||
|
||||
/*
|
||||
* Make sure nextXid is beyond any XID mentioned in the record.
|
||||
* This should be unnecessary, since any XID found here ought to
|
||||
* have other evidence in the XLOG, but let's be safe.
|
||||
* Make sure nextXid is beyond any XID mentioned in the record. This
|
||||
* should be unnecessary, since any XID found here ought to have other
|
||||
* evidence in the XLOG, but let's be safe.
|
||||
*/
|
||||
max_xid = record->xl_xid;
|
||||
for (i = 0; i < xlrec->nxids; i++)
|
||||
|
Reference in New Issue
Block a user