#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)); }