#include "access/xlog.h"

Go to the source code of this file.
| #define FirstMultiXactId ((MultiXactId) 1) |
Definition at line 23 of file multixact.h.
Referenced by do_start_worker(), ExtendMultiXactOffset(), GetMultiXactIdMembers(), GetNewMultiXactId(), GetOldestMultiXactId(), main(), MultiXactIdSetOldestMember(), MultiXactIdSetOldestVisible(), pg_get_multixact_members(), ReadNextMultiXactId(), relation_needs_vacanalyze(), SetMultiXactIdLimit(), and vacuum_set_xid_limits().
| #define InvalidMultiXactId ((MultiXactId) 0) |
Definition at line 22 of file multixact.h.
Referenced by CreateMultiXactId(), do_analyze_rel(), lazy_cleanup_index(), mXactCacheGetBySet(), reindex_index(), ResetSequence(), and swap_relation_files().
| #define MaxMultiXactId ((MultiXactId) 0xFFFFFFFF) |
Definition at line 24 of file multixact.h.
Referenced by SetMultiXactIdLimit().
| #define MaxMultiXactStatus MultiXactStatusUpdate |
Definition at line 49 of file multixact.h.
| #define MultiXactIdIsValid | ( | multi | ) | ((multi) != InvalidMultiXactId) |
Definition at line 26 of file multixact.h.
Referenced by AtPrepare_MultiXact(), CreateMultiXactId(), GetMultiXactIdMembers(), GetNewMultiXactId(), GetOldestMultiXactId(), heap_freeze_tuple(), MultiXactIdExpand(), MultiXactIdSetOldestMember(), MultiXactIdSetOldestVisible(), PostPrepare_MultiXact(), RelationSetNewRelfilenode(), SetMultiXactIdLimit(), swap_relation_files(), vac_truncate_clog(), vac_update_datfrozenxid(), and vac_update_relstats().
| #define NUM_MXACTMEMBER_BUFFERS 16 |
Definition at line 30 of file multixact.h.
Referenced by MultiXactShmemInit(), and MultiXactShmemSize().
| #define NUM_MXACTOFFSET_BUFFERS 8 |
Definition at line 29 of file multixact.h.
Referenced by MultiXactShmemInit(), MultiXactShmemSize(), and NumLWLocks().
| #define SizeOfMultiXactCreate (offsetof(xl_multixact_create, members)) |
Definition at line 76 of file multixact.h.
| #define XLOG_MULTIXACT_CREATE_ID 0x20 |
Definition at line 66 of file multixact.h.
Referenced by CreateMultiXactId(), multixact_desc(), and multixact_redo().
| #define XLOG_MULTIXACT_ZERO_MEM_PAGE 0x10 |
Definition at line 65 of file multixact.h.
Referenced by multixact_desc(), multixact_redo(), and ZeroMultiXactMemberPage().
| #define XLOG_MULTIXACT_ZERO_OFF_PAGE 0x00 |
Definition at line 64 of file multixact.h.
Referenced by multixact_desc(), multixact_redo(), and ZeroMultiXactOffsetPage().
| typedef struct MultiXactMember MultiXactMember |
| typedef struct xl_multixact_create xl_multixact_create |
| enum MultiXactStatus |
| MultiXactStatusForKeyShare | |
| MultiXactStatusForShare | |
| MultiXactStatusForNoKeyUpdate | |
| MultiXactStatusForUpdate | |
| MultiXactStatusNoKeyUpdate | |
| MultiXactStatusUpdate |
Definition at line 37 of file multixact.h.
{
MultiXactStatusForKeyShare = 0x00,
MultiXactStatusForShare = 0x01,
MultiXactStatusForNoKeyUpdate = 0x02,
MultiXactStatusForUpdate = 0x03,
/* an update that doesn't touch "key" columns */
MultiXactStatusNoKeyUpdate = 0x04,
/* other updates, and delete */
MultiXactStatusUpdate = 0x05
} MultiXactStatus;
| void AtEOXact_MultiXact | ( | void | ) |
Definition at line 1455 of file multixact.c.
References MyBackendId, OldestMemberMXactId, and OldestVisibleMXactId.
Referenced by AbortTransaction(), and CommitTransaction().
{
/*
* 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.
*/
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.
*/
MXactContext = NULL;
MXactCache = NULL;
}
| void AtPrepare_MultiXact | ( | void | ) |
Definition at line 1483 of file multixact.c.
References MultiXactIdIsValid, MyBackendId, OldestMemberMXactId, RegisterTwoPhaseRecord(), and TWOPHASE_RM_MULTIXACT_ID.
Referenced by PrepareTransaction().
{
MultiXactId myOldestMember = OldestMemberMXactId[MyBackendId];
if (MultiXactIdIsValid(myOldestMember))
RegisterTwoPhaseRecord(TWOPHASE_RM_MULTIXACT_ID, 0,
&myOldestMember, sizeof(MultiXactId));
}
| void BootStrapMultiXact | ( | void | ) |
Definition at line 1654 of file multixact.c.
References Assert, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, SimpleLruWritePage(), ZeroMultiXactMemberPage(), and ZeroMultiXactOffsetPage().
Referenced by BootStrapXLOG().
{
int slotno;
LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
/* Create and zero the first page of the offsets log */
slotno = ZeroMultiXactOffsetPage(0, false);
/* Make sure it's written out */
SimpleLruWritePage(MultiXactOffsetCtl, slotno);
Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
LWLockRelease(MultiXactOffsetControlLock);
LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
/* Create and zero the first page of the members log */
slotno = ZeroMultiXactMemberPage(0, false);
/* Make sure it's written out */
SimpleLruWritePage(MultiXactMemberCtl, slotno);
Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
LWLockRelease(MultiXactMemberControlLock);
}
| void CheckPointMultiXact | ( | void | ) |
Definition at line 1847 of file multixact.c.
References MultiXactMemberCtl, MultiXactOffsetCtl, and SimpleLruFlush().
Referenced by CheckPointGuts().
{
TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_START(true);
/* Flush dirty MultiXact pages to disk */
SimpleLruFlush(MultiXactOffsetCtl, true);
SimpleLruFlush(MultiXactMemberCtl, true);
TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(true);
}
| int GetMultiXactIdMembers | ( | MultiXactId | multi, | |
| MultiXactMember ** | xids, | |||
| bool | allow_old | |||
| ) |
Definition at line 1030 of file multixact.c.
References Assert, DEBUG1, DEBUG2, debug_elog3, ereport, errcode(), errmsg(), ERROR, FirstMultiXactId, i, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactIdSetOldestVisible(), MultiXactIdToOffsetEntry, MultiXactIdToOffsetPage, MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, MXACT_MEMBER_XACT_BITMASK, mXactCacheGetById(), mXactCachePut(), mxid_to_string(), MXOffsetToFlagsBitShift, MXOffsetToFlagsOffset, MXOffsetToMemberOffset, MXOffsetToMemberPage, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactId, palloc(), pg_usleep(), SimpleLruReadPage(), MultiXactMember::status, TransactionIdIsValid, and MultiXactMember::xid.
Referenced by Do_MultiXactIdWait(), GetMultiXactIdHintBits(), heap_lock_tuple(), MultiXactIdExpand(), MultiXactIdGetUpdateXid(), MultiXactIdIsRunning(), pg_get_multixact_members(), and pgrowlocks().
{
int pageno;
int prev_pageno;
int entryno;
int slotno;
MultiXactOffset *offptr;
MultiXactOffset offset;
int length;
int truelength;
int i;
MultiXactId oldestMXact;
MultiXactId nextMXact;
MultiXactId tmpMXact;
MultiXactOffset nextOffset;
MultiXactMember *ptr;
debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);
Assert(MultiXactIdIsValid(multi));
/* See if the MultiXactId is in the local cache */
length = mXactCacheGetById(multi, members);
if (length >= 0)
{
debug_elog3(DEBUG2, "GetMembers: found %s in the cache",
mxid_to_string(multi, length, *members));
return length;
}
/* Set our OldestVisibleMXactId[] entry if we didn't already */
MultiXactIdSetOldestVisible();
/*
* We check known limits on MultiXact before resorting to the SLRU area.
*
* An ID older than MultiXactState->oldestMultiXactId cannot possibly be
* useful; it should have already been frozen by vacuum. We've truncated
* the on-disk structures anyway. Returning the wrong values could lead to
* an incorrect visibility result. However, to support pg_upgrade we need
* to allow an empty set to be returned regardless, if the caller is
* willing to accept it; the caller is expected to check that it's an
* allowed condition (such as ensuring that the infomask bits set on the
* tuple are consistent with the pg_upgrade scenario). If the caller is
* expecting this to be called only on recently created multis, then we
* raise an error.
*
* Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is
* seen, it implies undetected ID wraparound has occurred. This raises
* a hard error.
*
* Shared lock is enough here since we aren't modifying any global state.
* Acquire it just long enough to grab the current counter values. We may
* need both nextMXact and nextOffset; see below.
*/
LWLockAcquire(MultiXactGenLock, LW_SHARED);
oldestMXact = MultiXactState->oldestMultiXactId;
nextMXact = MultiXactState->nextMXact;
nextOffset = MultiXactState->nextOffset;
LWLockRelease(MultiXactGenLock);
if (MultiXactIdPrecedes(multi, oldestMXact))
{
ereport(allow_old ? DEBUG1 : ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("MultiXactId %u does no longer exist -- apparent wraparound",
multi)));
return -1;
}
if (!MultiXactIdPrecedes(multi, nextMXact))
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("MultiXactId %u has not been created yet -- apparent wraparound",
multi)));
/*
* Find out the offset at which we need to start reading MultiXactMembers
* and the number of members in the multixact. We determine the latter as
* the difference between this multixact's starting offset and the next
* one's. However, there are some corner cases to worry about:
*
* 1. This multixact may be the latest one created, in which case there is
* no next one to look at. In this case the nextOffset value we just
* saved is the correct endpoint.
*
* 2. The next multixact may still be in process of being filled in: that
* is, another process may have done GetNewMultiXactId but not yet written
* the offset entry for that ID. In that scenario, it is guaranteed that
* the offset entry for that multixact exists (because GetNewMultiXactId
* won't release MultiXactGenLock until it does) but contains zero
* (because we are careful to pre-zero offset pages). Because
* GetNewMultiXactId will never return zero as the starting offset for a
* multixact, when we read zero as the next multixact's offset, we know we
* have this case. We sleep for a bit and try again.
*
* 3. Because GetNewMultiXactId increments offset zero to offset one to
* handle case #2, there is an ambiguity near the point of offset
* wraparound. If we see next multixact's offset is one, is that our
* multixact's actual endpoint, or did it end at zero with a subsequent
* increment? We handle this using the knowledge that if the zero'th
* member slot wasn't filled, it'll contain zero, and zero isn't a valid
* transaction ID so it can't be a multixact member. Therefore, if we
* read a zero from the members array, just ignore it.
*
* This is all pretty messy, but the mess occurs only in infrequent corner
* cases, so it seems better than holding the MultiXactGenLock for a long
* time on every multixact creation.
*/
retry:
LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
pageno = MultiXactIdToOffsetPage(multi);
entryno = MultiXactIdToOffsetEntry(multi);
slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
offptr += entryno;
offset = *offptr;
Assert(offset != 0);
/*
* Use the same increment rule as GetNewMultiXactId(), that is, don't
* handle wraparound explicitly until needed.
*/
tmpMXact = multi + 1;
if (nextMXact == tmpMXact)
{
/* Corner case 1: there is no next multixact */
length = nextOffset - offset;
}
else
{
MultiXactOffset nextMXOffset;
/* handle wraparound if needed */
if (tmpMXact < FirstMultiXactId)
tmpMXact = FirstMultiXactId;
prev_pageno = pageno;
pageno = MultiXactIdToOffsetPage(tmpMXact);
entryno = MultiXactIdToOffsetEntry(tmpMXact);
if (pageno != prev_pageno)
slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, tmpMXact);
offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
offptr += entryno;
nextMXOffset = *offptr;
if (nextMXOffset == 0)
{
/* Corner case 2: next multixact is still being filled in */
LWLockRelease(MultiXactOffsetControlLock);
pg_usleep(1000L);
goto retry;
}
length = nextMXOffset - offset;
}
LWLockRelease(MultiXactOffsetControlLock);
ptr = (MultiXactMember *) palloc(length * sizeof(MultiXactMember));
*members = ptr;
/* Now get the members themselves. */
LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
truelength = 0;
prev_pageno = -1;
for (i = 0; i < length; i++, offset++)
{
TransactionId *xactptr;
uint32 *flagsptr;
int flagsoff;
int bshift;
int memberoff;
pageno = MXOffsetToMemberPage(offset);
memberoff = MXOffsetToMemberOffset(offset);
if (pageno != prev_pageno)
{
slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
prev_pageno = pageno;
}
xactptr = (TransactionId *)
(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
if (!TransactionIdIsValid(*xactptr))
{
/* Corner case 3: we must be looking at unused slot zero */
Assert(offset == 0);
continue;
}
flagsoff = MXOffsetToFlagsOffset(offset);
bshift = MXOffsetToFlagsBitShift(offset);
flagsptr = (uint32 *) (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
ptr[truelength].xid = *xactptr;
ptr[truelength].status = (*flagsptr >> bshift) & MXACT_MEMBER_XACT_BITMASK;
truelength++;
}
LWLockRelease(MultiXactMemberControlLock);
/*
* Copy the result into the local cache.
*/
mXactCachePut(multi, truelength, ptr);
debug_elog3(DEBUG2, "GetMembers: no cache for %s",
mxid_to_string(multi, truelength, ptr));
return truelength;
}
| MultiXactId GetOldestMultiXactId | ( | void | ) |
Definition at line 2128 of file multixact.c.
References FirstMultiXactId, i, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactStateData::nextMXact, OldestMemberMXactId, and OldestVisibleMXactId.
Referenced by AddNewRelationTuple(), ExecuteTruncate(), vac_update_datfrozenxid(), and vacuum_set_xid_limits().
{
MultiXactId oldestMXact;
MultiXactId nextMXact;
int i;
/*
* 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.
*/
nextMXact = MultiXactState->nextMXact;
if (nextMXact < FirstMultiXactId)
nextMXact = FirstMultiXactId;
oldestMXact = nextMXact;
for (i = 1; i <= MaxOldestSlot; i++)
{
MultiXactId thisoldest;
thisoldest = OldestMemberMXactId[i];
if (MultiXactIdIsValid(thisoldest) &&
MultiXactIdPrecedes(thisoldest, oldestMXact))
oldestMXact = thisoldest;
thisoldest = OldestVisibleMXactId[i];
if (MultiXactIdIsValid(thisoldest) &&
MultiXactIdPrecedes(thisoldest, oldestMXact))
oldestMXact = thisoldest;
}
LWLockRelease(MultiXactGenLock);
return oldestMXact;
}
| void multixact_desc | ( | StringInfo | buf, | |
| uint8 | xl_info, | |||
| char * | rec | |||
| ) |
Definition at line 50 of file mxactdesc.c.
References appendStringInfo(), i, xl_multixact_create::members, xl_multixact_create::mid, xl_multixact_create::moff, xl_multixact_create::nmembers, out_member(), XLOG_MULTIXACT_CREATE_ID, XLOG_MULTIXACT_ZERO_MEM_PAGE, and XLOG_MULTIXACT_ZERO_OFF_PAGE.
{
uint8 info = xl_info & ~XLR_INFO_MASK;
if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
{
int pageno;
memcpy(&pageno, rec, sizeof(int));
appendStringInfo(buf, "zero offsets page: %d", pageno);
}
else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
{
int pageno;
memcpy(&pageno, rec, sizeof(int));
appendStringInfo(buf, "zero members page: %d", pageno);
}
else if (info == XLOG_MULTIXACT_CREATE_ID)
{
xl_multixact_create *xlrec = (xl_multixact_create *) rec;
int i;
appendStringInfo(buf, "create mxid %u offset %u nmembers %d: ", xlrec->mid,
xlrec->moff, xlrec->nmembers);
for (i = 0; i < xlrec->nmembers; i++)
out_member(buf, &xlrec->members[i]);
}
else
appendStringInfo(buf, "UNKNOWN");
}
| void multixact_redo | ( | XLogRecPtr | lsn, | |
| XLogRecord * | record | |||
| ) |
Definition at line 2343 of file multixact.c.
References Assert, elog, i, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), xl_multixact_create::members, xl_multixact_create::mid, xl_multixact_create::moff, MultiXactAdvanceNextMXact(), MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, VariableCacheData::nextXid, xl_multixact_create::nmembers, PANIC, RecordNewMultiXact(), ShmemVariableCache, SimpleLruWritePage(), TransactionIdAdvance, TransactionIdFollowsOrEquals(), TransactionIdPrecedes(), MultiXactMember::xid, XidGenLock, XLogRecord::xl_info, XLogRecord::xl_xid, XLOG_MULTIXACT_CREATE_ID, XLOG_MULTIXACT_ZERO_MEM_PAGE, XLOG_MULTIXACT_ZERO_OFF_PAGE, XLogRecGetData, XLR_BKP_BLOCK_MASK, ZeroMultiXactMemberPage(), and ZeroMultiXactOffsetPage().
{
uint8 info = record->xl_info & ~XLR_INFO_MASK;
/* Backup blocks are not used in multixact records */
Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
{
int pageno;
int slotno;
memcpy(&pageno, XLogRecGetData(record), sizeof(int));
LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
slotno = ZeroMultiXactOffsetPage(pageno, false);
SimpleLruWritePage(MultiXactOffsetCtl, slotno);
Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
LWLockRelease(MultiXactOffsetControlLock);
}
else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
{
int pageno;
int slotno;
memcpy(&pageno, XLogRecGetData(record), sizeof(int));
LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
slotno = ZeroMultiXactMemberPage(pageno, false);
SimpleLruWritePage(MultiXactMemberCtl, slotno);
Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
LWLockRelease(MultiXactMemberControlLock);
}
else if (info == XLOG_MULTIXACT_CREATE_ID)
{
xl_multixact_create *xlrec =
(xl_multixact_create *) XLogRecGetData(record);
TransactionId max_xid;
int i;
/* Store the data back into the SLRU files */
RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nmembers,
xlrec->members);
/* Make sure nextMXact/nextOffset are beyond what this record has */
MultiXactAdvanceNextMXact(xlrec->mid + 1,
xlrec->moff + xlrec->nmembers);
/*
* 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->nmembers; i++)
{
if (TransactionIdPrecedes(max_xid, xlrec->members[i].xid))
max_xid = xlrec->members[i].xid;
}
/*
* We don't expect anyone else to modify nextXid, hence startup
* process doesn't need to hold a lock while checking this. We still
* acquire the lock to modify it, though.
*/
if (TransactionIdFollowsOrEquals(max_xid,
ShmemVariableCache->nextXid))
{
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
ShmemVariableCache->nextXid = max_xid;
TransactionIdAdvance(ShmemVariableCache->nextXid);
LWLockRelease(XidGenLock);
}
}
else
elog(PANIC, "multixact_redo: unknown op code %u", info);
}
| void multixact_twophase_postabort | ( | TransactionId | xid, | |
| uint16 | info, | |||
| void * | recdata, | |||
| uint32 | len | |||
| ) |
Definition at line 1582 of file multixact.c.
References multixact_twophase_postcommit().
{
multixact_twophase_postcommit(xid, info, recdata, len);
}
| void multixact_twophase_postcommit | ( | TransactionId | xid, | |
| uint16 | info, | |||
| void * | recdata, | |||
| uint32 | len | |||
| ) |
Definition at line 1567 of file multixact.c.
References Assert, OldestMemberMXactId, and TwoPhaseGetDummyBackendId().
Referenced by multixact_twophase_postabort().
{
BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid);
Assert(len == sizeof(MultiXactId));
OldestMemberMXactId[dummyBackendId] = InvalidMultiXactId;
}
| void multixact_twophase_recover | ( | TransactionId | xid, | |
| uint16 | info, | |||
| void * | recdata, | |||
| uint32 | len | |||
| ) |
Definition at line 1546 of file multixact.c.
References Assert, OldestMemberMXactId, and TwoPhaseGetDummyBackendId().
{
BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid);
MultiXactId oldestMember;
/*
* Get the oldest member XID from the state file record, and set it in the
* OldestMemberMXactId slot reserved for this prepared transaction.
*/
Assert(len == sizeof(MultiXactId));
oldestMember = *((MultiXactId *) recdata);
OldestMemberMXactId[dummyBackendId] = oldestMember;
}
| void MultiXactAdvanceNextMXact | ( | MultiXactId | minMulti, | |
| MultiXactOffset | minMultiOffset | |||
| ) |
Definition at line 2008 of file multixact.c.
References DEBUG2, debug_elog3, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdPrecedes(), MultiXactOffsetPrecedes(), MultiXactStateData::nextMXact, and MultiXactStateData::nextOffset.
Referenced by multixact_redo(), and xlog_redo().
{
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
if (MultiXactIdPrecedes(MultiXactState->nextMXact, minMulti))
{
debug_elog3(DEBUG2, "MultiXact: setting next multi to %u", minMulti);
MultiXactState->nextMXact = minMulti;
}
if (MultiXactOffsetPrecedes(MultiXactState->nextOffset, minMultiOffset))
{
debug_elog3(DEBUG2, "MultiXact: setting next offset to %u",
minMultiOffset);
MultiXactState->nextOffset = minMultiOffset;
}
LWLockRelease(MultiXactGenLock);
}
| void MultiXactAdvanceOldest | ( | MultiXactId | oldestMulti, | |
| Oid | oldestMultiDB | |||
| ) |
Definition at line 2031 of file multixact.c.
References MultiXactIdPrecedes(), MultiXactStateData::oldestMultiXactId, and SetMultiXactIdLimit().
Referenced by vac_truncate_clog(), and xlog_redo().
{
if (MultiXactIdPrecedes(MultiXactState->oldestMultiXactId, oldestMulti))
SetMultiXactIdLimit(oldestMulti, oldestMultiDB);
}
| void MultiXactGetCheckptMulti | ( | bool | is_shutdown, | |
| MultiXactId * | nextMulti, | |||
| MultiXactOffset * | nextMultiOffset, | |||
| MultiXactId * | oldestMulti, | |||
| Oid * | oldestMultiDB | |||
| ) |
Definition at line 1825 of file multixact.c.
References DEBUG2, debug_elog6, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactDB, and MultiXactStateData::oldestMultiXactId.
Referenced by CreateCheckPoint().
{
LWLockAcquire(MultiXactGenLock, LW_SHARED);
*nextMulti = MultiXactState->nextMXact;
*nextMultiOffset = MultiXactState->nextOffset;
*oldestMulti = MultiXactState->oldestMultiXactId;
*oldestMultiDB = MultiXactState->oldestMultiXactDB;
LWLockRelease(MultiXactGenLock);
debug_elog6(DEBUG2,
"MultiXact: checkpoint is nextMulti %u, nextOffset %u, oldestMulti %u in DB %u",
*nextMulti, *nextMultiOffset, *oldestMulti, *oldestMultiDB);
}
| MultiXactId MultiXactIdCreate | ( | TransactionId | xid1, | |
| MultiXactStatus | status1, | |||
| TransactionId | xid2, | |||
| MultiXactStatus | status2 | |||
| ) |
Definition at line 324 of file multixact.c.
References Assert, AssertArg, CreateMultiXactId(), DEBUG2, debug_elog3, mXactCacheEnt::members, mxid_to_string(), MultiXactMember::status, TransactionIdEquals, TransactionIdIsValid, and MultiXactMember::xid.
Referenced by compute_new_xmax_infomask().
{
MultiXactId newMulti;
MultiXactMember members[2];
AssertArg(TransactionIdIsValid(xid1));
AssertArg(TransactionIdIsValid(xid2));
Assert(!TransactionIdEquals(xid1, xid2) || (status1 != status2));
/*
* 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.
*/
members[0].xid = xid1;
members[0].status = status1;
members[1].xid = xid2;
members[1].status = status2;
newMulti = CreateMultiXactId(2, members);
debug_elog3(DEBUG2, "Create: %s",
mxid_to_string(newMulti, 2, members));
return newMulti;
}
| MultiXactId MultiXactIdExpand | ( | MultiXactId | multi, | |
| TransactionId | xid, | |||
| MultiXactStatus | status | |||
| ) |
Definition at line 374 of file multixact.c.
References AssertArg, CreateMultiXactId(), DEBUG2, debug_elog3, debug_elog4, debug_elog5, GetMultiXactIdMembers(), i, mXactCacheEnt::members, MultiXactIdIsValid, mxstatus_to_string(), mXactCacheEnt::nmembers, palloc(), pfree(), MultiXactMember::status, TransactionIdDidCommit(), TransactionIdEquals, TransactionIdIsInProgress(), TransactionIdIsValid, and MultiXactMember::xid.
Referenced by compute_new_xmax_infomask().
{
MultiXactId newMulti;
MultiXactMember *members;
MultiXactMember *newMembers;
int nmembers;
int i;
int j;
AssertArg(MultiXactIdIsValid(multi));
AssertArg(TransactionIdIsValid(xid));
debug_elog5(DEBUG2, "Expand: received multi %u, xid %u status %s",
multi, xid, mxstatus_to_string(status));
/*
* Note: we don't allow for old multis here. The reason is that the
* only caller of this function does a check that the multixact is
* no longer running.
*/
nmembers = GetMultiXactIdMembers(multi, &members, false);
if (nmembers < 0)
{
MultiXactMember member;
/*
* The MultiXactId is obsolete. This can only happen if all the
* MultiXactId members stop running between the caller checking and
* passing it to us. It would be better to return that fact to the
* caller, but it would complicate the API and it's unlikely to happen
* too often, so just deal with it by creating a singleton MultiXact.
*/
member.xid = xid;
member.status = status;
newMulti = CreateMultiXactId(1, &member);
debug_elog4(DEBUG2, "Expand: %u has no members, create singleton %u",
multi, newMulti);
return newMulti;
}
/*
* If the TransactionId is already a member of the MultiXactId with the
* same status, just return the existing MultiXactId.
*/
for (i = 0; i < nmembers; i++)
{
if (TransactionIdEquals(members[i].xid, xid) &&
(members[i].status == status))
{
debug_elog4(DEBUG2, "Expand: %u is already a member of %u",
xid, multi);
pfree(members);
return multi;
}
}
/*
* Determine which of the members of the MultiXactId are still of interest.
* This is any running transaction, and also any transaction that grabbed
* something stronger than just a lock and was committed. (An update that
* aborted is of no interest here.)
*
* (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 = (MultiXactMember *)
palloc(sizeof(MultiXactMember) * (nmembers + 1));
for (i = 0, j = 0; i < nmembers; i++)
{
if (TransactionIdIsInProgress(members[i].xid) ||
((members[i].status > MultiXactStatusForUpdate) &&
TransactionIdDidCommit(members[i].xid)))
{
newMembers[j].xid = members[i].xid;
newMembers[j++].status = members[i].status;
}
}
newMembers[j].xid = xid;
newMembers[j++].status = status;
newMulti = CreateMultiXactId(j, newMembers);
pfree(members);
pfree(newMembers);
debug_elog3(DEBUG2, "Expand: returning new multi %u", newMulti);
return newMulti;
}
| bool MultiXactIdIsRunning | ( | MultiXactId | multi | ) |
Definition at line 480 of file multixact.c.
References DEBUG2, debug_elog2, debug_elog3, debug_elog4, GetMultiXactIdMembers(), i, mXactCacheEnt::members, mXactCacheEnt::nmembers, pfree(), TransactionIdIsCurrentTransactionId(), and TransactionIdIsInProgress().
Referenced by compute_new_xmax_infomask(), HeapTupleSatisfiesUpdate(), and HeapTupleSatisfiesVacuum().
{
MultiXactMember *members;
int nmembers;
int i;
debug_elog3(DEBUG2, "IsRunning %u?", multi);
/*
* "false" here means we assume our callers have checked that the given
* multi cannot possibly come from a pg_upgraded database.
*/
nmembers = GetMultiXactIdMembers(multi, &members, false);
if (nmembers < 0)
{
debug_elog2(DEBUG2, "IsRunning: no members");
return false;
}
/*
* Checking for myself is cheap compared to looking in shared memory;
* return true if any live subtransaction of the current top-level
* transaction is a member.
*
* This is not needed for correctness, it's just a fast path.
*/
for (i = 0; i < nmembers; i++)
{
if (TransactionIdIsCurrentTransactionId(members[i].xid))
{
debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i);
pfree(members);
return true;
}
}
/*
* 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
* cases nmembers should be small enough that it doesn't much matter.
*/
for (i = 0; i < nmembers; i++)
{
if (TransactionIdIsInProgress(members[i].xid))
{
debug_elog4(DEBUG2, "IsRunning: member %d (%u) is running",
i, members[i].xid);
pfree(members);
return true;
}
}
pfree(members);
debug_elog3(DEBUG2, "IsRunning: %u is not running", multi);
return false;
}
| bool MultiXactIdPrecedes | ( | MultiXactId | multi1, | |
| MultiXactId | multi2 | |||
| ) |
Definition at line 2305 of file multixact.c.
Referenced by do_start_worker(), GetMultiXactIdMembers(), GetNewMultiXactId(), GetOldestMultiXactId(), heap_freeze_tuple(), heap_tuple_needs_freeze(), MultiXactAdvanceNextMXact(), MultiXactAdvanceOldest(), MultiXactIdSetOldestVisible(), MultiXactOffsetPagePrecedes(), relation_needs_vacanalyze(), SetMultiXactIdLimit(), TruncateMultiXact(), vac_truncate_clog(), vac_update_datfrozenxid(), and vac_update_relstats().
{
int32 diff = (int32) (multi1 - multi2);
return (diff < 0);
}
| void MultiXactIdSetOldestMember | ( | void | ) |
Definition at line 554 of file multixact.c.
References DEBUG2, debug_elog4, FirstMultiXactId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MyBackendId, MultiXactStateData::nextMXact, and OldestMemberMXactId.
Referenced by heap_delete(), heap_lock_tuple(), heap_lock_updated_tuple(), and heap_update().
{
if (!MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]))
{
MultiXactId nextMXact;
/*
* 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.
*/
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.
*/
nextMXact = MultiXactState->nextMXact;
if (nextMXact < FirstMultiXactId)
nextMXact = FirstMultiXactId;
OldestMemberMXactId[MyBackendId] = nextMXact;
LWLockRelease(MultiXactGenLock);
debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = %u",
MyBackendId, nextMXact);
}
}
| void MultiXactSetNextMXact | ( | MultiXactId | nextMulti, | |
| MultiXactOffset | nextMultiOffset | |||
| ) |
Definition at line 1867 of file multixact.c.
References DEBUG2, debug_elog4, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactStateData::nextMXact, and MultiXactStateData::nextOffset.
Referenced by BootStrapXLOG(), StartupXLOG(), and xlog_redo().
{
debug_elog4(DEBUG2, "MultiXact: setting next multi to %u offset %u",
nextMulti, nextMultiOffset);
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
MultiXactState->nextMXact = nextMulti;
MultiXactState->nextOffset = nextMultiOffset;
LWLockRelease(MultiXactGenLock);
}
| void MultiXactShmemInit | ( | void | ) |
Definition at line 1610 of file multixact.c.
References Assert, DEBUG2, debug_elog2, IsUnderPostmaster, MemSet, MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, NUM_MXACTMEMBER_BUFFERS, NUM_MXACTOFFSET_BUFFERS, OldestMemberMXactId, OldestVisibleMXactId, MultiXactStateData::perBackendXactIds, SHARED_MULTIXACT_STATE_SIZE, ShmemInitStruct(), and SimpleLruInit().
Referenced by CreateSharedMemoryAndSemaphores().
{
bool found;
debug_elog2(DEBUG2, "Shared Memory Init for MultiXact");
MultiXactOffsetCtl->PagePrecedes = MultiXactOffsetPagePrecedes;
MultiXactMemberCtl->PagePrecedes = MultiXactMemberPagePrecedes;
SimpleLruInit(MultiXactOffsetCtl,
"MultiXactOffset Ctl", NUM_MXACTOFFSET_BUFFERS, 0,
MultiXactOffsetControlLock, "pg_multixact/offsets");
SimpleLruInit(MultiXactMemberCtl,
"MultiXactMember Ctl", NUM_MXACTMEMBER_BUFFERS, 0,
MultiXactMemberControlLock, "pg_multixact/members");
/* Initialize our shared state struct */
MultiXactState = ShmemInitStruct("Shared MultiXact State",
SHARED_MULTIXACT_STATE_SIZE,
&found);
if (!IsUnderPostmaster)
{
Assert(!found);
/* Make sure we zero out the per-backend state */
MemSet(MultiXactState, 0, SHARED_MULTIXACT_STATE_SIZE);
}
else
Assert(found);
/*
* Set up array pointers. Note that perBackendXactIds[0] is wasted space
* since we only use indexes 1..MaxOldestSlot in each array.
*/
OldestMemberMXactId = MultiXactState->perBackendXactIds;
OldestVisibleMXactId = OldestMemberMXactId + MaxOldestSlot;
}
| Size MultiXactShmemSize | ( | void | ) |
Definition at line 1594 of file multixact.c.
References add_size(), NUM_MXACTMEMBER_BUFFERS, NUM_MXACTOFFSET_BUFFERS, and SimpleLruShmemSize().
Referenced by CreateSharedMemoryAndSemaphores().
{
Size size;
#define SHARED_MULTIXACT_STATE_SIZE \
add_size(sizeof(MultiXactStateData), \
mul_size(sizeof(MultiXactId) * 2, MaxOldestSlot))
size = SHARED_MULTIXACT_STATE_SIZE;
size = add_size(size, SimpleLruShmemSize(NUM_MXACTOFFSET_BUFFERS, 0));
size = add_size(size, SimpleLruShmemSize(NUM_MXACTMEMBER_BUFFERS, 0));
return size;
}
| char* mxid_to_string | ( | MultiXactId | multi, | |
| int | nmembers, | |||
| MultiXactMember * | members | |||
| ) |
Definition at line 1424 of file multixact.c.
References appendStringInfo(), appendStringInfoChar(), buf, StringInfoData::data, i, initStringInfo(), MemoryContextStrdup(), mxstatus_to_string(), NULL, pfree(), status(), and TopMemoryContext.
Referenced by CreateMultiXactId(), GetMultiXactIdMembers(), MultiXactIdCreate(), mXactCacheGetById(), mXactCacheGetBySet(), and mXactCachePut().
{
static char *str = NULL;
StringInfoData buf;
int i;
if (str != NULL)
pfree(str);
initStringInfo(&buf);
appendStringInfo(&buf, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
mxstatus_to_string(members[0].status));
for (i = 1; i < nmembers; i++)
appendStringInfo(&buf, ", %u (%s)", members[i].xid,
mxstatus_to_string(members[i].status));
appendStringInfoChar(&buf, ']');
str = MemoryContextStrdup(TopMemoryContext, buf.data);
pfree(buf.data);
return str;
}
| void PostPrepare_MultiXact | ( | TransactionId | xid | ) |
Definition at line 1497 of file multixact.c.
References LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, MultiXactIdIsValid, MyBackendId, OldestMemberMXactId, OldestVisibleMXactId, and TwoPhaseGetDummyBackendId().
Referenced by PrepareTransaction().
{
MultiXactId myOldestMember;
/*
* Transfer our OldestMemberMXactId value to the slot reserved for the
* prepared transaction.
*/
myOldestMember = OldestMemberMXactId[MyBackendId];
if (MultiXactIdIsValid(myOldestMember))
{
BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid);
/*
* Even though storing MultiXactId is atomic, acquire lock to make
* sure others see both changes, not just the reset of the slot of the
* current backend. Using a volatile pointer might suffice, but this
* isn't a hot spot.
*/
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
OldestMemberMXactId[dummyBackendId] = myOldestMember;
OldestMemberMXactId[MyBackendId] = InvalidMultiXactId;
LWLockRelease(MultiXactGenLock);
}
/*
* We don't need to transfer OldestVisibleMXactId value, because the
* transaction is not going to be looking at any more multixacts once it's
* prepared.
*
* We assume that storing a MultiXactId is atomic and so we need not take
* MultiXactGenLock to do this.
*/
OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId;
/*
* Discard the local MultiXactId cache like in AtEOX_MultiXact
*/
MXactContext = NULL;
MXactCache = NULL;
}
| MultiXactId ReadNextMultiXactId | ( | void | ) |
Definition at line 647 of file multixact.c.
References FirstMultiXactId, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactGenLock, and MultiXactStateData::nextMXact.
Referenced by ATRewriteTables(), AutoVacWorkerMain(), do_start_worker(), and ExecRefreshMatView().
{
MultiXactId mxid;
/* XXX we could presumably do this without a lock. */
LWLockAcquire(MultiXactGenLock, LW_SHARED);
mxid = MultiXactState->nextMXact;
LWLockRelease(MultiXactGenLock);
if (mxid < FirstMultiXactId)
mxid = FirstMultiXactId;
return mxid;
}
| void SetMultiXactIdLimit | ( | MultiXactId | oldest_datminmxid, | |
| Oid | oldest_datoid | |||
| ) |
Definition at line 1884 of file multixact.c.
References Assert, DEBUG1, ereport, errhint(), errmsg(), FirstMultiXactId, get_database_name(), InRecovery, IsTransactionState(), IsUnderPostmaster, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MaxMultiXactId, MultiXactStateData::multiStopLimit, MultiXactStateData::multiVacLimit, MultiXactStateData::multiWarnLimit, MultiXactStateData::multiWrapLimit, MultiXactGenLock, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactStateData::nextMXact, MultiXactStateData::oldestMultiXactDB, MultiXactStateData::oldestMultiXactId, PMSIGNAL_START_AUTOVAC_LAUNCHER, SendPostmasterSignal(), and WARNING.
Referenced by BootStrapXLOG(), MultiXactAdvanceOldest(), StartupXLOG(), and xlog_redo().
{
MultiXactId multiVacLimit;
MultiXactId multiWarnLimit;
MultiXactId multiStopLimit;
MultiXactId multiWrapLimit;
MultiXactId curMulti;
Assert(MultiXactIdIsValid(oldest_datminmxid));
/*
* The place where we actually get into deep trouble is halfway around
* from the oldest potentially-existing XID/multi. (This calculation is
* probably off by one or two counts for Xids, because the special XIDs
* reduce the size of the loop a little bit. But we throw in plenty of
* slop below, so it doesn't matter.)
*/
multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1);
if (multiWrapLimit < FirstMultiXactId)
multiWrapLimit += FirstMultiXactId;
/*
* We'll refuse to continue assigning MultiXactIds once we get within 100
* multi of data loss.
*/
multiStopLimit = multiWrapLimit - 100;
if (multiStopLimit < FirstMultiXactId)
multiStopLimit -= FirstMultiXactId;
/*
* We'll start complaining loudly when we get within 10M multis of the stop
* point. This is kind of arbitrary, but if you let your gas gauge get
* down to 1% of full, would you be looking for the next gas station? We
* need to be fairly liberal about this number because there are lots of
* scenarios where most transactions are done by automatic clients that
* won't pay attention to warnings. (No, we're not gonna make this
* configurable. If you know enough to configure it, you know enough to
* not get in this kind of trouble in the first place.)
*/
multiWarnLimit = multiStopLimit - 10000000;
if (multiWarnLimit < FirstMultiXactId)
multiWarnLimit -= FirstMultiXactId;
/*
* We'll start trying to force autovacuums when oldest_datminmxid gets
* to be more than 200 million transactions old.
*/
multiVacLimit = oldest_datminmxid + 200000000;
if (multiVacLimit < FirstMultiXactId)
multiVacLimit += FirstMultiXactId;
/* Grab lock for just long enough to set the new limit values */
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
MultiXactState->oldestMultiXactId = oldest_datminmxid;
MultiXactState->oldestMultiXactDB = oldest_datoid;
MultiXactState->multiVacLimit = multiVacLimit;
MultiXactState->multiWarnLimit = multiWarnLimit;
MultiXactState->multiStopLimit = multiStopLimit;
MultiXactState->multiWrapLimit = multiWrapLimit;
curMulti = MultiXactState->nextMXact;
LWLockRelease(MultiXactGenLock);
/* Log the info */
ereport(DEBUG1,
(errmsg("MultiXactId wrap limit is %u, limited by database with OID %u",
multiWrapLimit, oldest_datoid)));
/*
* If past the autovacuum force point, immediately signal an autovac
* request. The reason for this is that autovac only processes one
* database per invocation. Once it's finished cleaning up the oldest
* database, it'll call here, and we'll signal the postmaster to start
* another iteration immediately if there are still any old databases.
*/
if (MultiXactIdPrecedes(multiVacLimit, curMulti) &&
IsUnderPostmaster && !InRecovery)
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
/* Give an immediate warning if past the wrap warn point */
if (MultiXactIdPrecedes(multiWarnLimit, curMulti) && !InRecovery)
{
char *oldest_datname;
/*
* We can be called when not inside a transaction, for example during
* StartupXLOG(). In such a case we cannot do database access, so we
* must just report the oldest DB's OID.
*
* Note: it's also possible that get_database_name fails and returns
* NULL, for example because the database just got dropped. We'll
* still warn, even though the warning might now be unnecessary.
*/
if (IsTransactionState())
oldest_datname = get_database_name(oldest_datoid);
else
oldest_datname = NULL;
if (oldest_datname)
ereport(WARNING,
(errmsg("database \"%s\" must be vacuumed before %u more MultiXactIds are used",
oldest_datname,
multiWrapLimit - curMulti),
errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
"You might also need to commit or roll back old prepared transactions.")));
else
ereport(WARNING,
(errmsg("database with OID %u must be vacuumed before %u more MultiXactIds are used",
oldest_datoid,
multiWrapLimit - curMulti),
errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
"You might also need to commit or roll back old prepared transactions.")));
}
}
| void ShutdownMultiXact | ( | void | ) |
Definition at line 1812 of file multixact.c.
References MultiXactMemberCtl, MultiXactOffsetCtl, and SimpleLruFlush().
Referenced by ShutdownXLOG().
{
/* Flush dirty MultiXact pages to disk */
TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_START(false);
SimpleLruFlush(MultiXactOffsetCtl, false);
SimpleLruFlush(MultiXactMemberCtl, false);
TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(false);
}
| void StartupMultiXact | ( | void | ) |
Definition at line 1731 of file multixact.c.
References LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MemSet, mXactCacheEnt::multi, MultiXactIdToOffsetEntry, MultiXactIdToOffsetPage, MultiXactMemberControlLock, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, MXOffsetToFlagsOffset, MXOffsetToMemberOffset, MXOffsetToMemberPage, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, and SimpleLruReadPage().
Referenced by StartupXLOG().
{
MultiXactId multi = MultiXactState->nextMXact;
MultiXactOffset offset = MultiXactState->nextOffset;
int pageno;
int entryno;
int flagsoff;
/* Clean up offsets state */
LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
/*
* Initialize our idea of the latest page number.
*/
pageno = MultiXactIdToOffsetPage(multi);
MultiXactOffsetCtl->shared->latest_page_number = pageno;
/*
* 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;
slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
offptr += entryno;
MemSet(offptr, 0, BLCKSZ - (entryno * sizeof(MultiXactOffset)));
MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
}
LWLockRelease(MultiXactOffsetControlLock);
/* And the same for members */
LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
/*
* Initialize our idea of the latest page number.
*/
pageno = MXOffsetToMemberPage(offset);
MultiXactMemberCtl->shared->latest_page_number = pageno;
/*
* Zero out the remainder of the current members page. See notes in
* TrimCLOG() for motivation.
*/
flagsoff = MXOffsetToFlagsOffset(offset);
if (flagsoff != 0)
{
int slotno;
TransactionId *xidptr;
int memberoff;
memberoff = MXOffsetToMemberOffset(offset);
slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset);
xidptr = (TransactionId *)
(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
MemSet(xidptr, 0, BLCKSZ - memberoff);
/*
* Note: we don't need to zero out the flag bits in the remaining
* members of the current group, because they are always reset before
* writing.
*/
MultiXactMemberCtl->shared->page_dirty[slotno] = true;
}
LWLockRelease(MultiXactMemberControlLock);
}
| void TruncateMultiXact | ( | MultiXactId | cutoff_multi | ) |
Definition at line 2201 of file multixact.c.
References mxtruncinfo::earliestExistingPage, LWLockRelease(), MultiXactIdPrecedes(), MultiXactIdToOffsetEntry, MultiXactIdToOffsetPage, MultiXactMemberCtl, MultiXactOffsetControlLock, MultiXactOffsetCtl, MXOffsetToMemberPage, SimpleLruReadPage_ReadOnly(), SimpleLruTruncate(), SlruScanDirCbFindEarliest(), and SlruScanDirectory().
Referenced by vac_truncate_clog().
{
MultiXactOffset oldestOffset;
mxtruncinfo trunc;
MultiXactId earliest;
/*
* Note we can't just plow ahead with the truncation; it's possible that
* there are no segments to truncate, which is a problem because we are
* going to attempt to read the offsets page to determine where to truncate
* the members SLRU. So we first scan the directory to determine the
* earliest offsets page number that we can read without error.
*/
trunc.earliestExistingPage = -1;
SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
earliest = trunc.earliestExistingPage * MULTIXACT_OFFSETS_PER_PAGE;
/* nothing to do */
if (MultiXactIdPrecedes(oldestMXact, earliest))
return;
/*
* First, compute the safe truncation point for MultiXactMember.
* This is the starting offset of the multixact we were passed
* as MultiXactOffset cutoff.
*/
{
int pageno;
int slotno;
int entryno;
MultiXactOffset *offptr;
/* lock is acquired by SimpleLruReadPage_ReadOnly */
pageno = MultiXactIdToOffsetPage(oldestMXact);
entryno = MultiXactIdToOffsetEntry(oldestMXact);
slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno,
oldestMXact);
offptr = (MultiXactOffset *)
MultiXactOffsetCtl->shared->page_buffer[slotno];
offptr += entryno;
oldestOffset = *offptr;
LWLockRelease(MultiXactOffsetControlLock);
}
/* truncate MultiXactOffset */
SimpleLruTruncate(MultiXactOffsetCtl,
MultiXactIdToOffsetPage(oldestMXact));
/* truncate MultiXactMembers and we're done */
SimpleLruTruncate(MultiXactMemberCtl,
MXOffsetToMemberPage(oldestOffset));
}
1.7.1